diff --git a/README.md b/README.md index 8108829..cde4d42 100644 --- a/README.md +++ b/README.md @@ -657,6 +657,19 @@ Graph points are saved in the browser in **localstorage** to be persistant, clea _There are many issues with the graph component currently and work is ongoing. Consider helping us out with development!_ +Upgrade From Version 2.2.3 to 2.3.1 + +A significant change that breaks backwards compatibility with 2.2.3 and earlier based applications has been made in this upgrade. + +All of the control type and control color enums and definitions have been moved inside of the control class public name space. This means that the usage of the Type and Color values now requires the Control::Type and Control::Color prefixes + +To do this: + +- Replace all instances of ControlType with Control::Type +- Replace all instances of ControlColor with Control::Color +- If your code has been created directly using the control type without the scope, then you will need to add Control::Type:: in front of the type usage. Ex. Control::Type::Button +- If your code has been created directly using the control color without the scope, then you will need to add Control::Color:: in front of the color usage. Ex. Control::Color::Carrot + ### Captive Portal ESPUI will redirect all unknown URLs it is asked for to the 'root' of the local HTTP server instead of responding with an HTTP code 404. This makes it act as a simple 'captive portal'. Note you must also set up the ESP to be a DNS server that responds to all DNS requests with the IP address of the ESP. This only effective when the ESP is acting as a WiFi hotspot in AP mode and assigning itself as the DNS server to connected clients. diff --git a/examples/completeExample/completeExample.cpp b/examples/completeExample/completeExample.cpp index 6833310..ef98e29 100644 --- a/examples/completeExample/completeExample.cpp +++ b/examples/completeExample/completeExample.cpp @@ -96,59 +96,59 @@ void setUpUI() { * Tab: Basic Controls * This tab contains all the basic ESPUI controls, and shows how to read and update them at runtime. *-----------------------------------------------------------------------------------------------------------*/ - auto maintab = ESPUI.addControl(Tab, "", "Basic controls"); + auto maintab = ESPUI.addControl(Control::Type::Tab, "", "Basic controls"); - ESPUI.addControl(Separator, "General controls", "", None, maintab); - ESPUI.addControl(Button, "Button", "Button 1", Alizarin, maintab, extendedCallback, (void*)19); - mainLabel = ESPUI.addControl(Label, "Label", "Label text", Emerald, maintab, generalCallback); - mainSwitcher = ESPUI.addControl(Switcher, "Switcher", "", Sunflower, maintab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "General controls", "", Control::Color::None, maintab); + ESPUI.addControl(Control::Type::Button, "Button", "Button 1", Control::Color::Alizarin, maintab, extendedCallback, (void*)19); + mainLabel = ESPUI.addControl(Control::Type::Label, "Label", "Label text", Control::Color::Emerald, maintab, generalCallback); + mainSwitcher = ESPUI.addControl(Control::Type::Switcher, "Switcher", "", Control::Color::Sunflower, maintab, generalCallback); //Sliders default to being 0 to 100, but if you want different limits you can add a Min and Max control - mainSlider = ESPUI.addControl(Slider, "Slider", "200", Turquoise, maintab, generalCallback); - ESPUI.addControl(Min, "", "10", None, mainSlider); - ESPUI.addControl(Max, "", "400", None, mainSlider); + mainSlider = ESPUI.addControl(Control::Type::Slider, "Slider", "200", Control::Color::Turquoise, maintab, generalCallback); + ESPUI.addControl(Control::Type::Min, "", "10", Control::Color::None, mainSlider); + ESPUI.addControl(Control::Type::Max, "", "400", Control::Color::None, mainSlider); //These are the values for the selector's options. (Note that they *must* be declared static //so that the storage is allocated in global memory and not just on the stack of this function.) static String optionValues[] {"Value 1", "Value 2", "Value 3", "Value 4", "Value 5"}; - auto mainselector = ESPUI.addControl(Select, "Selector", "Selector", Wetasphalt, maintab, generalCallback); + auto mainselector = ESPUI.addControl(Control::Type::Select, "Selector", "Selector", Control::Color::Wetasphalt, maintab, generalCallback); for(auto const& v : optionValues) { - ESPUI.addControl(Option, v.c_str(), v, None, mainselector); + ESPUI.addControl(Control::Type::Option, v.c_str(), v, Control::Color::None, mainselector); } - mainText = ESPUI.addControl(Text, "Text Input", "Initial value", Alizarin, maintab, generalCallback); + mainText = ESPUI.addControl(Control::Type::Text, "Text Input", "Initial value", Control::Color::Alizarin, maintab, generalCallback); //Number inputs also accept Min and Max components, but you should still validate the values. - mainNumber = ESPUI.addControl(Number, "Number Input", "42", Emerald, maintab, generalCallback); - ESPUI.addControl(Min, "", "10", None, mainNumber); - ESPUI.addControl(Max, "", "50", None, mainNumber); + mainNumber = ESPUI.addControl(Control::Type::Number, "Number Input", "42", Control::Color::Emerald, maintab, generalCallback); + ESPUI.addControl(Control::Type::Min, "", "10", Control::Color::None, mainNumber); + ESPUI.addControl(Control::Type::Max, "", "50", Control::Color::None, mainNumber); - ESPUI.addControl(Separator, "Updates", "", None, maintab); + ESPUI.addControl(Control::Type::Separator, "Updates", "", Control::Color::None, maintab); //This button will update all the updatable controls on this tab to random values - mainScrambleButton = ESPUI.addControl(Button, "Scramble Values", "Scramble Values", Carrot, maintab, scrambleCallback); - ESPUI.addControl(Switcher, "Constant updates", "0", Carrot, maintab, updateCallback); - mainTime = ESPUI.addControl(Time, "", "", None, 0, generalCallback); - ESPUI.addControl(Button, "Get Time", "Get Time", Carrot, maintab, getTimeCallback); + mainScrambleButton = ESPUI.addControl(Control::Type::Button, "Scramble Values", "Scramble Values", Control::Color::Carrot, maintab, scrambleCallback); + ESPUI.addControl(Control::Type::Switcher, "Constant updates", "0", Control::Color::Carrot, maintab, updateCallback); + mainTime = ESPUI.addControl(Control::Type::Time, "", "", Control::Color::None, 0, generalCallback); + ESPUI.addControl(Control::Type::Button, "Get Time", "Get Time", Control::Color::Carrot, maintab, getTimeCallback); - ESPUI.addControl(Separator, "Control Pads", "", None, maintab); - ESPUI.addControl(Pad, "Normal", "", Peterriver, maintab, generalCallback); - ESPUI.addControl(PadWithCenter, "With center", "", Peterriver, maintab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Control Pads", "", Control::Color::None, maintab); + ESPUI.addControl(Control::Type::Pad, "Normal", "", Control::Color::Peterriver, maintab, generalCallback); + ESPUI.addControl(Control::Type::PadWithCenter, "With center", "", Control::Color::Peterriver, maintab, generalCallback); /* * Tab: Colours * This tab shows all the basic colours *-----------------------------------------------------------------------------------------------------------*/ - auto colourtab = ESPUI.addControl(Tab, "", "Colours"); - ESPUI.addControl(Button, "Alizarin", "Alizarin", Alizarin, colourtab, generalCallback); - ESPUI.addControl(Button, "Turquoise", "Turquoise", Turquoise, colourtab, generalCallback); - ESPUI.addControl(Button, "Emerald", "Emerald", Emerald, colourtab, generalCallback); - ESPUI.addControl(Button, "Peterriver", "Peterriver", Peterriver, colourtab, generalCallback); - ESPUI.addControl(Button, "Wetasphalt", "Wetasphalt", Wetasphalt, colourtab, generalCallback); - ESPUI.addControl(Button, "Sunflower", "Sunflower", Sunflower, colourtab, generalCallback); - ESPUI.addControl(Button, "Carrot", "Carrot", Carrot, colourtab, generalCallback); - ESPUI.addControl(Button, "Dark", "Dark", Dark, colourtab, generalCallback); + auto colourtab = ESPUI.addControl(Control::Type::Tab, "", "Colours"); + ESPUI.addControl(Control::Type::Button, "Alizarin", "Alizarin", Control::Color::Alizarin, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Turquoise", "Turquoise", Control::Color::Turquoise, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Emerald", "Emerald", Control::Color::Emerald, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Peterriver", "Peterriver", Control::Color::Peterriver, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Wetasphalt", "Wetasphalt", Control::Color::Wetasphalt, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Sunflower", "Sunflower", Control::Color::Sunflower, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Carrot", "Carrot", Control::Color::Carrot, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Dark", "Dark", Control::Color::Dark, colourtab, generalCallback); /* @@ -156,24 +156,24 @@ void setUpUI() { * This tab shows off how inline CSS styles can be applied to elements and panels in order * to customise the look of the UI. *-----------------------------------------------------------------------------------------------------------*/ - auto styletab = ESPUI.addControl(Tab, "", "Styled controls"); - styleButton = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); - styleLabel = ESPUI.addControl(Label, "Styled Label", "This is a label", Alizarin, styletab, generalCallback); - styleSwitcher = ESPUI.addControl(Switcher, "Styled Switcher", "1", Alizarin, styletab, generalCallback); - styleSlider = ESPUI.addControl(Slider, "Styled Slider", "0", Alizarin, styletab, generalCallback); + auto styletab = ESPUI.addControl(Control::Type::Tab, "", "Styled controls"); + styleButton = ESPUI.addControl(Control::Type::Button, "Styled Button", "Button", Control::Color::Alizarin, styletab, generalCallback); + styleLabel = ESPUI.addControl(Control::Type::Label, "Styled Label", "This is a label", Control::Color::Alizarin, styletab, generalCallback); + styleSwitcher = ESPUI.addControl(Control::Type::Switcher, "Styled Switcher", "1", Control::Color::Alizarin, styletab, generalCallback); + styleSlider = ESPUI.addControl(Control::Type::Slider, "Styled Slider", "0", Control::Color::Alizarin, styletab, generalCallback); //This button will randomise the colours of the above controls to show updating of inline styles - ESPUI.addControl(Button, "Randomise Colours", "Randomise Colours", Sunflower, styletab, styleCallback); + ESPUI.addControl(Control::Type::Button, "Randomise Colours", "Randomise Colours", Control::Color::Sunflower, styletab, styleCallback); - ESPUI.addControl(Separator, "Other styling examples", "", None, styletab); - styleButton2 = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Other styling examples", "", Control::Color::None, styletab); + styleButton2 = ESPUI.addControl(Control::Type::Button, "Styled Button", "Button", Control::Color::Alizarin, styletab, generalCallback); ESPUI.setPanelStyle(styleButton2, "background: linear-gradient(90deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%); border-bottom: #555;"); ESPUI.setElementStyle(styleButton2, "border-radius: 2em; border: 3px solid black; width: 30%; background-color: #8df;"); - styleSlider2 = ESPUI.addControl(Slider, "Styled Slider", "0", Dark, styletab, generalCallback); + styleSlider2 = ESPUI.addControl(Control::Type::Slider, "Styled Slider", "0", Control::Color::Dark, styletab, generalCallback); ESPUI.setElementStyle(styleSlider2, "background: linear-gradient(to right, red, orange, yellow, green, blue);"); - styleLabel2 = ESPUI.addControl(Label, "Styled Label", "This is a label", Dark, styletab, generalCallback); + styleLabel2 = ESPUI.addControl(Control::Type::Label, "Styled Label", "This is a label", Control::Color::Dark, styletab, generalCallback); ESPUI.setElementStyle(styleLabel2, "text-shadow: 3px 3px #74b1ff, 6px 6px #c64ad7; font-size: 60px; font-variant-caps: small-caps; background-color: unset; color: #c4f0bb; -webkit-text-stroke: 1px black;"); @@ -182,14 +182,14 @@ void setUpUI() { * This tab shows how multiple control can be grouped into the same panel through the use of the * parentControl value. This also shows how to add labels to grouped controls, and how to use vertical controls. *-----------------------------------------------------------------------------------------------------------*/ - auto grouptab = ESPUI.addControl(Tab, "", "Grouped controls"); + auto grouptab = ESPUI.addControl(Control::Type::Tab, "", "Grouped controls"); //The parent of this button is a tab, so it will create a new panel with one control. - auto groupbutton = ESPUI.addControl(Button, "Button Group", "Button A", Dark, grouptab, generalCallback); + auto groupbutton = ESPUI.addControl(Control::Type::Button, "Button Group", "Button A", Control::Color::Dark, grouptab, generalCallback); //However the parent of this button is another control, so therefore no new panel is //created and the button is added to the existing panel. - ESPUI.addControl(Button, "", "Button B", Alizarin, groupbutton, generalCallback); - ESPUI.addControl(Button, "", "Button C", Alizarin, groupbutton, generalCallback); + ESPUI.addControl(Control::Type::Button, "", "Button B", Control::Color::Alizarin, groupbutton, generalCallback); + ESPUI.addControl(Control::Type::Button, "", "Button C", Control::Color::Alizarin, groupbutton, generalCallback); //Sliders can be grouped as well @@ -197,65 +197,65 @@ void setUpUI() { //We need this CSS style rule, which will remove the label's background and ensure that it takes up the entire width of the panel String clearLabelStyle = "background-color: unset; width: 100%;"; //First we add the main slider to create a panel - auto groupsliders = ESPUI.addControl(Slider, "Slider Group", "10", Dark, grouptab, generalCallback); + auto groupsliders = ESPUI.addControl(Control::Type::Slider, "Slider Group", "10", Control::Color::Dark, grouptab, generalCallback); //Then we add a label and set its style to the clearLabelStyle. Here we've just given it the name "A" - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupsliders), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, groupsliders), clearLabelStyle); //We can now continue to add additional sliders and labels - ESPUI.addControl(Slider, "", "20", None, groupsliders, generalCallback); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupsliders), clearLabelStyle); - ESPUI.addControl(Slider, "", "30", None, groupsliders, generalCallback); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupsliders), clearLabelStyle); + ESPUI.addControl(Control::Type::Slider, "", "20", Control::Color::None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, groupsliders), clearLabelStyle); + ESPUI.addControl(Control::Type::Slider, "", "30", Control::Color::None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, groupsliders), clearLabelStyle); //We can also usefully group switchers. - auto groupswitcher = ESPUI.addControl(Switcher, "Switcher Group", "0", Dark, grouptab, generalCallback); - ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); - ESPUI.addControl(Switcher, "", "0", Sunflower, groupswitcher, generalCallback); - ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); + auto groupswitcher = ESPUI.addControl(Control::Type::Switcher, "Switcher Group", "0", Control::Color::Dark, grouptab, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "1", Control::Color::Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "1", Control::Color::Sunflower, groupswitcher, generalCallback); //To label these switchers we need to first go onto a "new line" below the line of switchers //To do this we add an empty label set to be clear and full width (with our clearLabelStyle) - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, groupswitcher), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "", Control::Color::None, groupswitcher), clearLabelStyle); //We will now need another label style. This one sets its width to the same as a switcher (and turns off the background) String switcherLabelStyle = "width: 60px; margin-left: .3rem; margin-right: .3rem; background-color: unset;"; //We can now just add the styled labels. - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "D", Control::Color::None, groupswitcher), switcherLabelStyle); //You can mix and match different control types, but the results might sometimes //need additional styling to lay out nicely. - auto grouplabel = ESPUI.addControl(Label, "Mixed Group", "Main label", Dark, grouptab); - auto grouplabel2 = ESPUI.addControl(Label, "", "Secondary label", Emerald, grouplabel); - ESPUI.addControl(Button, "", "Button D", Alizarin, grouplabel, generalCallback); - ESPUI.addControl(Switcher, "", "1", Sunflower, grouplabel, generalCallback); + auto grouplabel = ESPUI.addControl(Control::Type::Label, "Mixed Group", "Main label", Control::Color::Dark, grouptab); + auto grouplabel2 = ESPUI.addControl(Control::Type::Label, "", "Secondary label", Control::Color::Emerald, grouplabel); + ESPUI.addControl(Control::Type::Button, "", "Button D", Control::Color::Alizarin, grouplabel, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "1", Control::Color::Sunflower, grouplabel, generalCallback); ESPUI.setElementStyle(grouplabel2, "font-size: x-large; font-family: serif;"); //Some controls can even support vertical orientation, currently Switchers and Sliders - ESPUI.addControl(Separator, "Vertical controls", "", None, grouptab); - auto vertgroupswitcher = ESPUI.addControl(Switcher, "Vertical Switcher Group", "0", Dark, grouptab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Vertical controls", "", Control::Color::None, grouptab); + auto vertgroupswitcher = ESPUI.addControl(Control::Type::Switcher, "Vertical Switcher Group", "0", Control::Color::Dark, grouptab, generalCallback); ESPUI.setVertical(vertgroupswitcher); //On the following lines we wrap the value returned from addControl and send it straight to setVertical - ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::None, vertgroupswitcher, generalCallback)); //The mechanism for labelling vertical switchers is the same as we used above for horizontal ones - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupswitcher), clearLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "", Control::Color::None, vertgroupswitcher), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "D", Control::Color::None, vertgroupswitcher), switcherLabelStyle); - auto vertgroupslider = ESPUI.addControl(Slider, "Vertical Slider Group", "15", Dark, grouptab, generalCallback); + auto vertgroupslider = ESPUI.addControl(Control::Type::Slider, "Vertical Slider Group", "15", Control::Color::Dark, grouptab, generalCallback); ESPUI.setVertical(vertgroupslider); - ESPUI.setVertical(ESPUI.addControl(Slider, "", "25", None, vertgroupslider, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Slider, "", "35", None, vertgroupslider, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Slider, "", "45", None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Slider, "", "25", Control::Color::None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Slider, "", "35", Control::Color::None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Slider, "", "45", Control::Color::None, vertgroupslider, generalCallback)); //The mechanism for labelling vertical sliders is the same as we used above for switchers - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupslider), clearLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupslider), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupslider), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupslider), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "", Control::Color::None, vertgroupslider), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "D", Control::Color::None, vertgroupslider), switcherLabelStyle); //Note that combining vertical and horizontal sliders is going to result in very messy layout! @@ -263,29 +263,29 @@ void setUpUI() { * Tab: Example UI * An example UI for the documentation *-----------------------------------------------------------------------------------------------------------*/ - auto exampletab = ESPUI.addControl(Tab, "Example", "Example"); - ESPUI.addControl(Separator, "Control and Status", "", None, exampletab); - ESPUI.addControl(Switcher, "Power", "1", Alizarin, exampletab, generalCallback); - ESPUI.addControl(Label, "Status", "System status: OK", Wetasphalt, exampletab, generalCallback); + auto exampletab = ESPUI.addControl(Control::Type::Tab, "Example", "Example"); + ESPUI.addControl(Control::Type::Separator, "Control and Status", "", Control::Color::None, exampletab); + ESPUI.addControl(Control::Type::Switcher, "Power", "1", Control::Color::Alizarin, exampletab, generalCallback); + ESPUI.addControl(Control::Type::Label, "Status", "System status: OK", Control::Color::Wetasphalt, exampletab, generalCallback); - ESPUI.addControl(Separator, "Settings", "", None, exampletab); - ESPUI.addControl(PadWithCenter, "Attitude Control", "", Dark, exampletab, generalCallback); - auto examplegroup1 = ESPUI.addControl(Button, "Activate Features", "Feature A", Carrot, exampletab, generalCallback); - ESPUI.addControl(Button, "Activate Features", "Feature B", Carrot, examplegroup1, generalCallback); - ESPUI.addControl(Button, "Activate Features", "Feature C", Carrot, examplegroup1, generalCallback); - ESPUI.addControl(Slider, "Value control", "45", Peterriver, exampletab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Settings", "", Control::Color::None, exampletab); + ESPUI.addControl(Control::Type::PadWithCenter, "Attitude Control", "", Control::Color::Dark, exampletab, generalCallback); + auto examplegroup1 = ESPUI.addControl(Control::Type::Button, "Activate Features", "Feature A", Control::Color::Carrot, exampletab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Activate Features", "Feature B", Control::Color::Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Control::Type::Button, "Activate Features", "Feature C", Control::Color::Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Control::Type::Slider, "Value control", "45", Control::Color::Peterriver, exampletab, generalCallback); /* * Tab: WiFi Credentials * You use this tab to enter the SSID and password of a wifi network to autoconnect to. *-----------------------------------------------------------------------------------------------------------*/ - auto wifitab = ESPUI.addControl(Tab, "", "WiFi Credentials"); - wifi_ssid_text = ESPUI.addControl(Text, "SSID", "", Alizarin, wifitab, textCallback); + auto wifitab = ESPUI.addControl(Control::Type::Tab, "", "WiFi Credentials"); + wifi_ssid_text = ESPUI.addControl(Control::Type::Text, "SSID", "", Control::Color::Alizarin, wifitab, textCallback); //Note that adding a "Max" control to a text control sets the max length - ESPUI.addControl(Max, "", "32", None, wifi_ssid_text); - wifi_pass_text = ESPUI.addControl(Text, "Password", "", Alizarin, wifitab, textCallback); - ESPUI.addControl(Max, "", "64", None, wifi_pass_text); - ESPUI.addControl(Button, "Save", "Save", Peterriver, wifitab, enterWifiDetailsCallback); + ESPUI.addControl(Control::Type::Max, "", "32", Control::Color::None, wifi_ssid_text); + wifi_pass_text = ESPUI.addControl(Control::Type::Text, "Password", "", Control::Color::Alizarin, wifitab, textCallback); + ESPUI.addControl(Control::Type::Max, "", "64", Control::Color::None, wifi_pass_text); + ESPUI.addControl(Control::Type::Button, "Save", "Save", Control::Color::Peterriver, wifitab, enterWifiDetailsCallback); //Finally, start up the UI. diff --git a/examples/completeLambda/completeLambda.ino b/examples/completeLambda/completeLambda.ino index a435d93..c2b1810 100644 --- a/examples/completeLambda/completeLambda.ino +++ b/examples/completeLambda/completeLambda.ino @@ -36,7 +36,7 @@ #ifndef CORE_MOCK #ifndef MMU_IRAM_HEAP #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) -#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) +#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(Control::Type::...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning then check http:///heap #endif // MMU_IRAM_HEAP #ifndef DEBUG_ESP_OOM @@ -92,37 +92,37 @@ void setUpUI() { * Tab: Basic Controls * This tab contains all the basic ESPUI controls, and shows how to read and update them at runtime. *-----------------------------------------------------------------------------------------------------------*/ - auto maintab = ESPUI.addControl(Tab, "", "Basic controls"); + auto maintab = ESPUI.addControl(Control::Type::Tab, "", "Basic controls"); - ESPUI.addControl(Separator, "General controls", "", None, maintab); - ESPUI.addControl(Button, "Button", "Button 1", Alizarin, maintab, [](Control *sender, int type){ paramCallback(sender, type, 19); }); - mainLabel = ESPUI.addControl(Label, "Label", "Label text", Emerald, maintab, generalCallback); - mainSwitcher = ESPUI.addControl(Switcher, "Switcher", "", Sunflower, maintab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "General controls", "", Control::Color::None, maintab); + ESPUI.addControl(Control::Type::Button, "Button", "Button 1", Control::Color::Alizarin, maintab, [](Control *sender, int type){ paramCallback(sender, type, 19); }); + mainLabel = ESPUI.addControl(Control::Type::Label, "Label", "Label text", Control::Color::Emerald, maintab, generalCallback); + mainSwitcher = ESPUI.addControl(Control::Type::Switcher, "Switcher", "", Control::Color::Sunflower, maintab, generalCallback); //Sliders default to being 0 to 100, but if you want different limits you can add a Min and Max control - mainSlider = ESPUI.addControl(Slider, "Slider", "200", Turquoise, maintab, generalCallback); - ESPUI.addControl(Min, "", "10", None, mainSlider); - ESPUI.addControl(Max, "", "400", None, mainSlider); + mainSlider = ESPUI.addControl(Control::Type::Slider, "Slider", "200", Control::Color::Turquoise, maintab, generalCallback); + ESPUI.addControl(Control::Type::Min, "", "10", Control::Color::None, mainSlider); + ESPUI.addControl(Control::Type::Max, "", "400", Control::Color::None, mainSlider); //These are the values for the selector's options. (Note that they *must* be declared static //so that the storage is allocated in global memory and not just on the stack of this function.) static String optionValues[] {"Value 1", "Value 2", "Value 3", "Value 4", "Value 5"}; - auto mainselector = ESPUI.addControl(Select, "Selector", "Selector", Wetasphalt, maintab, generalCallback); + auto mainselector = ESPUI.addControl(Control::Type::Select, "Selector", "Selector", Control::Color::Wetasphalt, maintab, generalCallback); for(auto const& v : optionValues) { - ESPUI.addControl(Option, v.c_str(), v, None, mainselector); + ESPUI.addControl(Control::Type::Option, v.c_str(), v, Control::Color::None, mainselector); } - mainText = ESPUI.addControl(Text, "Text Input", "Initial value", Alizarin, maintab, generalCallback); + mainText = ESPUI.addControl(Control::Type::Text, "Text Input", "Initial value", Control::Color::Alizarin, maintab, generalCallback); //Number inputs also accept Min and Max components, but you should still validate the values. - mainNumber = ESPUI.addControl(Number, "Number Input", "42", Emerald, maintab, generalCallback); - ESPUI.addControl(Min, "", "10", None, mainNumber); - ESPUI.addControl(Max, "", "50", None, mainNumber); + mainNumber = ESPUI.addControl(Control::Type::Number, "Number Input", "42", Control::Color::Emerald, maintab, generalCallback); + ESPUI.addControl(Control::Type::Min, "", "10", Control::Color::None, mainNumber); + ESPUI.addControl(Control::Type::Max, "", "50", Control::Color::None, mainNumber); - ESPUI.addControl(Separator, "Updates", "", None, maintab); + ESPUI.addControl(Control::Type::Separator, "Updates", "", Control::Color::None, maintab); //This button will update all the updatable controls on this tab to random values - mainScrambleButton = ESPUI.addControl(Button, "Scramble Values", "Scramble Values", Carrot, maintab, + mainScrambleButton = ESPUI.addControl(Control::Type::Button, "Scramble Values", "Scramble Values", Control::Color::Carrot, maintab, //This callback updates the "values" of a bunch of controls [](Control *sender, int type) { static char rndString1[10]; @@ -145,38 +145,38 @@ void setUpUI() { } }); - ESPUI.addControl(Switcher, "Constant updates", "0", Carrot, maintab, + ESPUI.addControl(Control::Type::Switcher, "Constant updates", "0", Control::Color::Carrot, maintab, [](Control *sender, int type) { updates = (sender->value.toInt() > 0); }); - mainTime = ESPUI.addControl(Time, "", "", None, 0, generalCallback); + mainTime = ESPUI.addControl(Control::Type::Time, "", "", Control::Color::None, 0, generalCallback); - ESPUI.addControl(Button, "Get Time", "Get Time", Carrot, maintab, + ESPUI.addControl(Control::Type::Button, "Get Time", "Get Time", Control::Color::Carrot, maintab, [](Control *sender, int type) { if(type == B_UP) { ESPUI.updateTime(mainTime); } }); - ESPUI.addControl(Separator, "Control Pads", "", None, maintab); - ESPUI.addControl(Pad, "Normal", "", Peterriver, maintab, generalCallback); - ESPUI.addControl(PadWithCenter, "With center", "", Peterriver, maintab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Control Pads", "", Control::Color::None, maintab); + ESPUI.addControl(Control::Type::Pad, "Normal", "", Control::Color::Peterriver, maintab, generalCallback); + ESPUI.addControl(Control::Type::PadWithCenter, "With center", "", Control::Color::Peterriver, maintab, generalCallback); /* * Tab: Colours * This tab shows all the basic colours *-----------------------------------------------------------------------------------------------------------*/ - auto colourtab = ESPUI.addControl(Tab, "", "Colours"); - ESPUI.addControl(Button, "Alizarin", "Alizarin", Alizarin, colourtab, generalCallback); - ESPUI.addControl(Button, "Turquoise", "Turquoise", Turquoise, colourtab, generalCallback); - ESPUI.addControl(Button, "Emerald", "Emerald", Emerald, colourtab, generalCallback); - ESPUI.addControl(Button, "Peterriver", "Peterriver", Peterriver, colourtab, generalCallback); - ESPUI.addControl(Button, "Wetasphalt", "Wetasphalt", Wetasphalt, colourtab, generalCallback); - ESPUI.addControl(Button, "Sunflower", "Sunflower", Sunflower, colourtab, generalCallback); - ESPUI.addControl(Button, "Carrot", "Carrot", Carrot, colourtab, generalCallback); - ESPUI.addControl(Button, "Dark", "Dark", Dark, colourtab, generalCallback); + auto colourtab = ESPUI.addControl(Control::Type::Tab, "", "Colours"); + ESPUI.addControl(Control::Type::Button, "Alizarin", "Alizarin", Control::Color::Alizarin, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Turquoise", "Turquoise", Control::Color::Turquoise, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Emerald", "Emerald", Control::Color::Emerald, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Peterriver", "Peterriver", Control::Color::Peterriver, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Wetasphalt", "Wetasphalt", Control::Color::Wetasphalt, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Sunflower", "Sunflower", Control::Color::Sunflower, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Carrot", "Carrot", Control::Color::Carrot, colourtab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Dark", "Dark", Control::Color::Dark, colourtab, generalCallback); /* @@ -184,14 +184,14 @@ void setUpUI() { * This tab shows off how inline CSS styles can be applied to elements and panels in order * to customise the look of the UI. *-----------------------------------------------------------------------------------------------------------*/ - auto styletab = ESPUI.addControl(Tab, "", "Styled controls"); - styleButton = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); - styleLabel = ESPUI.addControl(Label, "Styled Label", "This is a label", Alizarin, styletab, generalCallback); - styleSwitcher = ESPUI.addControl(Switcher, "Styled Switcher", "1", Alizarin, styletab, generalCallback); - styleSlider = ESPUI.addControl(Slider, "Styled Slider", "0", Alizarin, styletab, generalCallback); + auto styletab = ESPUI.addControl(Control::Type::Tab, "", "Styled controls"); + styleButton = ESPUI.addControl(Control::Type::Button, "Styled Button", "Button", Control::Color::Alizarin, styletab, generalCallback); + styleLabel = ESPUI.addControl(Control::Type::Label, "Styled Label", "This is a label", Control::Color::Alizarin, styletab, generalCallback); + styleSwitcher = ESPUI.addControl(Control::Type::Switcher, "Styled Switcher", "1", Control::Color::Alizarin, styletab, generalCallback); + styleSlider = ESPUI.addControl(Control::Type::Slider, "Styled Slider", "0", Control::Color::Alizarin, styletab, generalCallback); //This button will randomise the colours of the above controls to show updating of inline styles - ESPUI.addControl(Button, "Randomise Colours", "Randomise Colours", Sunflower, styletab, + ESPUI.addControl(Control::Type::Button, "Randomise Colours", "Randomise Colours", Control::Color::Sunflower, styletab, //This callback generates and applies inline styles to a bunch of controls to change their colour. //The styles created are of the form: // "border-bottom: #999 3px solid; background-color: #aabbcc;" @@ -218,15 +218,15 @@ void setUpUI() { } }); - ESPUI.addControl(Separator, "Other styling examples", "", None, styletab); - styleButton2 = ESPUI.addControl(Button, "Styled Button", "Button", Alizarin, styletab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Other styling examples", "", Control::Color::None, styletab); + styleButton2 = ESPUI.addControl(Control::Type::Button, "Styled Button", "Button", Control::Color::Alizarin, styletab, generalCallback); ESPUI.setPanelStyle(styleButton2, "background: linear-gradient(90deg, rgba(131,58,180,1) 0%, rgba(253,29,29,1) 50%, rgba(252,176,69,1) 100%); border-bottom: #555;"); ESPUI.setElementStyle(styleButton2, "border-radius: 2em; border: 3px solid black; width: 30%; background-color: #8df;"); - styleSlider2 = ESPUI.addControl(Slider, "Styled Slider", "0", Dark, styletab, generalCallback); + styleSlider2 = ESPUI.addControl(Control::Type::Slider, "Styled Slider", "0", Control::Color::Dark, styletab, generalCallback); ESPUI.setElementStyle(styleSlider2, "background: linear-gradient(to right, red, orange, yellow, green, blue);"); - styleLabel2 = ESPUI.addControl(Label, "Styled Label", "This is a label", Dark, styletab, generalCallback); + styleLabel2 = ESPUI.addControl(Control::Type::Label, "Styled Label", "This is a label", Control::Color::Dark, styletab, generalCallback); ESPUI.setElementStyle(styleLabel2, "text-shadow: 3px 3px #74b1ff, 6px 6px #c64ad7; font-size: 60px; font-variant-caps: small-caps; background-color: unset; color: #c4f0bb; -webkit-text-stroke: 1px black;"); @@ -235,14 +235,14 @@ void setUpUI() { * This tab shows how multiple control can be grouped into the same panel through the use of the * parentControl value. This also shows how to add labels to grouped controls, and how to use vertical controls. *-----------------------------------------------------------------------------------------------------------*/ - auto grouptab = ESPUI.addControl(Tab, "", "Grouped controls"); + auto grouptab = ESPUI.addControl(Control::Type::Tab, "", "Grouped controls"); //The parent of this button is a tab, so it will create a new panel with one control. - auto groupbutton = ESPUI.addControl(Button, "Button Group", "Button A", Dark, grouptab, generalCallback); + auto groupbutton = ESPUI.addControl(Control::Type::Button, "Button Group", "Button A", Control::Color::Dark, grouptab, generalCallback); //However the parent of this button is another control, so therefore no new panel is //created and the button is added to the existing panel. - ESPUI.addControl(Button, "", "Button B", Alizarin, groupbutton, generalCallback); - ESPUI.addControl(Button, "", "Button C", Alizarin, groupbutton, generalCallback); + ESPUI.addControl(Control::Type::Button, "", "Button B", Control::Color::Alizarin, groupbutton, generalCallback); + ESPUI.addControl(Control::Type::Button, "", "Button C", Control::Color::Alizarin, groupbutton, generalCallback); //Sliders can be grouped as well @@ -250,65 +250,65 @@ void setUpUI() { //We need this CSS style rule, which will remove the label's background and ensure that it takes up the entire width of the panel String clearLabelStyle = "background-color: unset; width: 100%;"; //First we add the main slider to create a panel - auto groupsliders = ESPUI.addControl(Slider, "Slider Group", "10", Dark, grouptab, generalCallback); + auto groupsliders = ESPUI.addControl(Control::Type::Slider, "Slider Group", "10", Control::Color::Dark, grouptab, generalCallback); //Then we add a label and set its style to the clearLabelStyle. Here we've just given it the name "A" - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupsliders), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, groupsliders), clearLabelStyle); //We can now continue to add additional sliders and labels - ESPUI.addControl(Slider, "", "20", None, groupsliders, generalCallback); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupsliders), clearLabelStyle); - ESPUI.addControl(Slider, "", "30", None, groupsliders, generalCallback); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupsliders), clearLabelStyle); + ESPUI.addControl(Control::Type::Slider, "", "20", Control::Color::None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, groupsliders), clearLabelStyle); + ESPUI.addControl(Control::Type::Slider, "", "30", Control::Color::None, groupsliders, generalCallback); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, groupsliders), clearLabelStyle); //We can also usefully group switchers. - auto groupswitcher = ESPUI.addControl(Switcher, "Switcher Group", "0", Dark, grouptab, generalCallback); - ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); - ESPUI.addControl(Switcher, "", "0", Sunflower, groupswitcher, generalCallback); - ESPUI.addControl(Switcher, "", "1", Sunflower, groupswitcher, generalCallback); + auto groupswitcher = ESPUI.addControl(Control::Type::Switcher, "Switcher Group", "0", Control::Color::Dark, grouptab, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "1", Control::Color::Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::Sunflower, groupswitcher, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "1", Control::Color::Sunflower, groupswitcher, generalCallback); //To label these switchers we need to first go onto a "new line" below the line of switchers //To do this we add an empty label set to be clear and full width (with our clearLabelStyle) - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, groupswitcher), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "", Control::Color::None, groupswitcher), clearLabelStyle); //We will now need another label style. This one sets its width to the same as a switcher (and turns off the background) String switcherLabelStyle = "width: 60px; margin-left: .3rem; margin-right: .3rem; background-color: unset;"; //We can now just add the styled labels. - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, groupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, groupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, groupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, groupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "D", Control::Color::None, groupswitcher), switcherLabelStyle); //You can mix and match different control types, but the results might sometimes //need additional styling to lay out nicely. - auto grouplabel = ESPUI.addControl(Label, "Mixed Group", "Main label", Dark, grouptab); - auto grouplabel2 = ESPUI.addControl(Label, "", "Secondary label", Emerald, grouplabel); - ESPUI.addControl(Button, "", "Button D", Alizarin, grouplabel, generalCallback); - ESPUI.addControl(Switcher, "", "1", Sunflower, grouplabel, generalCallback); + auto grouplabel = ESPUI.addControl(Control::Type::Label, "Mixed Group", "Main label", Control::Color::Dark, grouptab); + auto grouplabel2 = ESPUI.addControl(Control::Type::Label, "", "Secondary label", Control::Color::Emerald, grouplabel); + ESPUI.addControl(Control::Type::Button, "", "Button D", Control::Color::Alizarin, grouplabel, generalCallback); + ESPUI.addControl(Control::Type::Switcher, "", "1", Control::Color::Sunflower, grouplabel, generalCallback); ESPUI.setElementStyle(grouplabel2, "font-size: x-large; font-family: serif;"); //Some controls can even support vertical orientation, currently Switchers and Sliders - ESPUI.addControl(Separator, "Vertical controls", "", None, grouptab); - auto vertgroupswitcher = ESPUI.addControl(Switcher, "Vertical Switcher Group", "0", Dark, grouptab, generalCallback); + ESPUI.addControl(Control::Type::Separator, "Vertical controls", "", Control::Color::None, grouptab); + auto vertgroupswitcher = ESPUI.addControl(Control::Type::Switcher, "Vertical Switcher Group", "0", Control::Color::Dark, grouptab, generalCallback); ESPUI.setVertical(vertgroupswitcher); //On the following lines we wrap the value returned from addControl and send it straight to setVertical - ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Switcher, "", "0", None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::None, vertgroupswitcher, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Switcher, "", "0", Control::Color::None, vertgroupswitcher, generalCallback)); //The mechanism for labelling vertical switchers is the same as we used above for horizontal ones - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupswitcher), clearLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupswitcher), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "", Control::Color::None, vertgroupswitcher), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, vertgroupswitcher), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "D", Control::Color::None, vertgroupswitcher), switcherLabelStyle); - auto vertgroupslider = ESPUI.addControl(Slider, "Vertical Slider Group", "15", Dark, grouptab, generalCallback); + auto vertgroupslider = ESPUI.addControl(Control::Type::Slider, "Vertical Slider Group", "15", Control::Color::Dark, grouptab, generalCallback); ESPUI.setVertical(vertgroupslider); - ESPUI.setVertical(ESPUI.addControl(Slider, "", "25", None, vertgroupslider, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Slider, "", "35", None, vertgroupslider, generalCallback)); - ESPUI.setVertical(ESPUI.addControl(Slider, "", "45", None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Slider, "", "25", Control::Color::None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Slider, "", "35", Control::Color::None, vertgroupslider, generalCallback)); + ESPUI.setVertical(ESPUI.addControl(Control::Type::Slider, "", "45", Control::Color::None, vertgroupslider, generalCallback)); //The mechanism for labelling vertical sliders is the same as we used above for switchers - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "", None, vertgroupslider), clearLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "A", None, vertgroupslider), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "B", None, vertgroupslider), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "C", None, vertgroupslider), switcherLabelStyle); - ESPUI.setElementStyle(ESPUI.addControl(Label, "", "D", None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "", Control::Color::None, vertgroupslider), clearLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "A", Control::Color::None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "B", Control::Color::None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "C", Control::Color::None, vertgroupslider), switcherLabelStyle); + ESPUI.setElementStyle(ESPUI.addControl(Control::Type::Label, "", "D", Control::Color::None, vertgroupslider), switcherLabelStyle); //Note that combining vertical and horizontal sliders is going to result in very messy layout! @@ -316,29 +316,29 @@ void setUpUI() { * Tab: Example UI * An example UI for the documentation *-----------------------------------------------------------------------------------------------------------*/ - auto exampletab = ESPUI.addControl(Tab, "Example", "Example"); - ESPUI.addControl(Separator, "Control and Status", "", None, exampletab); - ESPUI.addControl(Switcher, "Power", "1", Alizarin, exampletab, generalCallback); - ESPUI.addControl(Label, "Status", "System status: OK", Wetasphalt, exampletab, generalCallback); - - ESPUI.addControl(Separator, "Settings", "", None, exampletab); - ESPUI.addControl(PadWithCenter, "Attitude Control", "", Dark, exampletab, generalCallback); - auto examplegroup1 = ESPUI.addControl(Button, "Activate Features", "Feature A", Carrot, exampletab, generalCallback); - ESPUI.addControl(Button, "Activate Features", "Feature B", Carrot, examplegroup1, generalCallback); - ESPUI.addControl(Button, "Activate Features", "Feature C", Carrot, examplegroup1, generalCallback); - ESPUI.addControl(Slider, "Value control", "45", Peterriver, exampletab, generalCallback); + auto exampletab = ESPUI.addControl(Control::Type::Tab, "Example", "Example"); + ESPUI.addControl(Control::Type::Separator, "Control and Status", "", Control::Color::None, exampletab); + ESPUI.addControl(Control::Type::Switcher, "Power", "1", Control::Color::Alizarin, exampletab, generalCallback); + ESPUI.addControl(Control::Type::Label, "Status", "System status: OK", Control::Color::Wetasphalt, exampletab, generalCallback); + + ESPUI.addControl(Control::Type::Separator, "Settings", "", Control::Color::None, exampletab); + ESPUI.addControl(Control::Type::PadWithCenter, "Attitude Control", "", Control::Color::Dark, exampletab, generalCallback); + auto examplegroup1 = ESPUI.addControl(Control::Type::Button, "Activate Features", "Feature A", Control::Color::Carrot, exampletab, generalCallback); + ESPUI.addControl(Control::Type::Button, "Activate Features", "Feature B", Control::Color::Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Control::Type::Button, "Activate Features", "Feature C", Control::Color::Carrot, examplegroup1, generalCallback); + ESPUI.addControl(Control::Type::Slider, "Value control", "45", Control::Color::Peterriver, exampletab, generalCallback); /* * Tab: WiFi Credentials * You use this tab to enter the SSID and password of a wifi network to autoconnect to. *-----------------------------------------------------------------------------------------------------------*/ - auto wifitab = ESPUI.addControl(Tab, "", "WiFi Credentials"); - wifi_ssid_text = ESPUI.addControl(Text, "SSID", "", Alizarin, wifitab, textCallback); + auto wifitab = ESPUI.addControl(Control::Type::Tab, "", "WiFi Credentials"); + wifi_ssid_text = ESPUI.addControl(Control::Type::Text, "SSID", "", Control::Color::Alizarin, wifitab, textCallback); //Note that adding a "Max" control to a text control sets the max length - ESPUI.addControl(Max, "", "32", None, wifi_ssid_text); - wifi_pass_text = ESPUI.addControl(Text, "Password", "", Alizarin, wifitab, textCallback); - ESPUI.addControl(Max, "", "64", None, wifi_pass_text); - ESPUI.addControl(Button, "Save", "Save", Peterriver, wifitab, + ESPUI.addControl(Control::Type::Max, "", "32", Control::Color::None, wifi_ssid_text); + wifi_pass_text = ESPUI.addControl(Control::Type::Text, "Password", "", Control::Color::Alizarin, wifitab, textCallback); + ESPUI.addControl(Control::Type::Max, "", "64", Control::Color::None, wifi_pass_text); + ESPUI.addControl(Control::Type::Button, "Save", "Save", Control::Color::Peterriver, wifitab, [](Control *sender, int type) { if(type == B_UP) { Serial.println("Saving credentials to EPROM..."); diff --git a/examples/gui-generic-api/gui-generic-api.ino b/examples/gui-generic-api/gui-generic-api.ino index 556596b..4fc39b5 100644 --- a/examples/gui-generic-api/gui-generic-api.ino +++ b/examples/gui-generic-api/gui-generic-api.ino @@ -77,7 +77,7 @@ void buttonExample(Control* sender, int type, void* param) Serial.println("Status: Start"); ESPUI.updateControlValue(status, "Start"); - ESPUI.getControl(button1)->color = ControlColor::Carrot; + ESPUI.getControl(button1)->color = Control::Color::Carrot; ESPUI.updateControl(button1); break; @@ -85,7 +85,7 @@ void buttonExample(Control* sender, int type, void* param) Serial.println("Status: Stop"); ESPUI.updateControlValue(status, "Stop"); - ESPUI.getControl(button1)->color = ControlColor::Peterriver; + ESPUI.getControl(button1)->color = Control::Color::Peterriver; ESPUI.updateControl(button1); break; } @@ -252,33 +252,33 @@ void setup(void) { HeapSelectIram doAllocationsInIRAM; #endif - status = ESPUI.addControl(ControlType::Label, "Status:", "Stop", ControlColor::Turquoise); + status = ESPUI.addControl(Control::Type::Label, "Status:", "Stop", Control::Color::Turquoise); uint16_t select1 = ESPUI.addControl( - ControlType::Select, "Select:", "", ControlColor::Alizarin, Control::noParent, &selectExample); + Control::Type::Select, "Select:", "", Control::Color::Alizarin, Control::noParent, &selectExample); - ESPUI.addControl(ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1); - ESPUI.addControl(ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1); - ESPUI.addControl(ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1); + ESPUI.addControl(Control::Type::Option, "Option1", "Opt1", Control::Color::Alizarin, select1); + ESPUI.addControl(Control::Type::Option, "Option2", "Opt2", Control::Color::Alizarin, select1); + ESPUI.addControl(Control::Type::Option, "Option3", "Opt3", Control::Color::Alizarin, select1); ESPUI.addControl( - ControlType::Text, "Text Test:", "a Text Field", ControlColor::Alizarin, Control::noParent, &textCall); + Control::Type::Text, "Text Test:", "a Text Field", Control::Color::Alizarin, Control::noParent, &textCall); - millisLabelId = ESPUI.addControl(ControlType::Label, "Millis:", "0", ControlColor::Emerald, Control::noParent); + millisLabelId = ESPUI.addControl(Control::Type::Label, "Millis:", "0", Control::Color::Emerald, Control::noParent); button1 = ESPUI.addControl( - ControlType::Button, "Push Button", "Press", ControlColor::Peterriver, Control::noParent, &buttonCallback); + Control::Type::Button, "Push Button", "Press", Control::Color::Peterriver, Control::noParent, &buttonCallback); ESPUI.addControl( - ControlType::Button, "Other Button", "Press", ControlColor::Wetasphalt, Control::noParent, &buttonExample, (void*)19); + Control::Type::Button, "Other Button", "Press", Control::Color::Wetasphalt, Control::noParent, &buttonExample, (void*)19); ESPUI.addControl( - ControlType::PadWithCenter, "Pad with center", "", ControlColor::Sunflower, Control::noParent, &padExample); - ESPUI.addControl(ControlType::Pad, "Pad without center", "", ControlColor::Carrot, Control::noParent, &padExample); + Control::Type::PadWithCenter, "Pad with center", "", Control::Color::Sunflower, Control::noParent, &padExample); + ESPUI.addControl(Control::Type::Pad, "Pad without center", "", Control::Color::Carrot, Control::noParent, &padExample); switchOne = ESPUI.addControl( - ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, Control::noParent, &switchExample); + Control::Type::Switcher, "Switch one", "", Control::Color::Alizarin, Control::noParent, &switchExample); ESPUI.addControl( - ControlType::Switcher, "Switch two", "", ControlColor::None, Control::noParent, &otherSwitchExample); - ESPUI.addControl(ControlType::Slider, "Slider one", "30", ControlColor::Alizarin, Control::noParent, &slider); - ESPUI.addControl(ControlType::Slider, "Slider two", "100", ControlColor::Alizarin, Control::noParent, &slider); - ESPUI.addControl(ControlType::Number, "Number:", "50", ControlColor::Alizarin, Control::noParent, &numberCall); + Control::Type::Switcher, "Switch two", "", Control::Color::None, Control::noParent, &otherSwitchExample); + ESPUI.addControl(Control::Type::Slider, "Slider one", "30", Control::Color::Alizarin, Control::noParent, &slider); + ESPUI.addControl(Control::Type::Slider, "Slider two", "100", Control::Color::Alizarin, Control::noParent, &slider); + ESPUI.addControl(Control::Type::Number, "Number:", "50", Control::Color::Alizarin, Control::noParent, &numberCall); /* * .begin loads and serves all files from PROGMEM directly. diff --git a/examples/gui/gui.ino b/examples/gui/gui.ino index 71ab831..4e1a619 100644 --- a/examples/gui/gui.ino +++ b/examples/gui/gui.ino @@ -14,7 +14,7 @@ DNSServer dnsServer; #ifndef CORE_MOCK #ifndef MMU_IRAM_HEAP #warning Try MMU option '2nd heap shared' in 'tools' IDE menu (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#option-summary) -#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) +#warning use decorators: { HeapSelectIram doAllocationsInIRAM; ESPUI.addControl(Control::Type::...) ... } (cf. https://arduino-esp8266.readthedocs.io/en/latest/mmu.html#how-to-select-heap) #warning then check http:///heap #endif // MMU_IRAM_HEAP #ifndef DEBUG_ESP_OOM @@ -242,20 +242,20 @@ void setup(void) { HeapSelectIram doAllocationsInIRAM; #endif - statusLabelId = ESPUI.label("Status:", ControlColor::Turquoise, "Stop"); - millisLabelId = ESPUI.label("Millis:", ControlColor::Emerald, "0"); - ESPUI.button("Push Button", &buttonCallback, ControlColor::Peterriver, "Press"); - ESPUI.button("Other Button", &buttonExample, ControlColor::Wetasphalt, "Press", (void*)19); - ESPUI.padWithCenter("Pad with center", &padExample, ControlColor::Sunflower); - ESPUI.pad("Pad without center", &padExample, ControlColor::Carrot); - testSwitchId = ESPUI.switcher("Switch one", &switchExample, ControlColor::Alizarin, false); - ESPUI.switcher("Switch two", &otherSwitchExample, ControlColor::None, true); - ESPUI.slider("Slider one", &slider, ControlColor::Alizarin, 30); - ESPUI.slider("Slider two", &slider, ControlColor::None, 100); - ESPUI.text("Text Test:", &textCall, ControlColor::Alizarin, "a Text Field"); - ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10); - - graphId = ESPUI.graph("Graph Test", ControlColor::Wetasphalt); + statusLabelId = ESPUI.label("Status:", Control::Color::Turquoise, "Stop"); + millisLabelId = ESPUI.label("Millis:", Control::Color::Emerald, "0"); + ESPUI.button("Push Button", &buttonCallback, Control::Color::Peterriver, "Press"); + ESPUI.button("Other Button", &buttonExample, Control::Color::Wetasphalt, "Press", (void*)19); + ESPUI.padWithCenter("Pad with center", &padExample, Control::Color::Sunflower); + ESPUI.pad("Pad without center", &padExample, Control::Color::Carrot); + testSwitchId = ESPUI.switcher("Switch one", &switchExample, Control::Color::Alizarin, false); + ESPUI.switcher("Switch two", &otherSwitchExample, Control::Color::None, true); + ESPUI.slider("Slider one", &slider, Control::Color::Alizarin, 30); + ESPUI.slider("Slider two", &slider, Control::Color::None, 100); + ESPUI.text("Text Test:", &textCall, Control::Color::Alizarin, "a Text Field"); + ESPUI.number("Numbertest", &numberCall, Control::Color::Alizarin, 5, 0, 10); + + graphId = ESPUI.graph("Graph Test", Control::Color::Wetasphalt); /* * .begin loads and serves all files from PROGMEM directly. diff --git a/examples/tabbedGui/tabbedGui.ino b/examples/tabbedGui/tabbedGui.ino index 4831a6b..560af61 100644 --- a/examples/tabbedGui/tabbedGui.ino +++ b/examples/tabbedGui/tabbedGui.ino @@ -76,7 +76,7 @@ void buttonExample(Control* sender, int type, void* param) Serial.println("Status: Start"); ESPUI.updateControlValue(status, "Start"); - ESPUI.getControl(button1)->color = ControlColor::Carrot; + ESPUI.getControl(button1)->color = Control::Color::Carrot; ESPUI.updateControl(button1); break; @@ -84,7 +84,7 @@ void buttonExample(Control* sender, int type, void* param) Serial.println("Status: Stop"); ESPUI.updateControlValue(status, "Stop"); - ESPUI.getControl(button1)->color = ControlColor::Peterriver; + ESPUI.getControl(button1)->color = Control::Color::Peterriver; ESPUI.updateControl(button1); break; } @@ -250,33 +250,33 @@ void setup(void) { HeapSelectIram doAllocationsInIRAM; #endif - uint16_t tab1 = ESPUI.addControl(ControlType::Tab, "Settings 1", "Settings 1"); - uint16_t tab2 = ESPUI.addControl(ControlType::Tab, "Settings 2", "Settings 2"); - uint16_t tab3 = ESPUI.addControl(ControlType::Tab, "Settings 3", "Settings 3"); + uint16_t tab1 = ESPUI.addControl(Control::Type::Tab, "Settings 1", "Settings 1"); + uint16_t tab2 = ESPUI.addControl(Control::Type::Tab, "Settings 2", "Settings 2"); + uint16_t tab3 = ESPUI.addControl(Control::Type::Tab, "Settings 3", "Settings 3"); // shown above all tabs - status = ESPUI.addControl(ControlType::Label, "Status:", "Stop", ControlColor::Turquoise); + status = ESPUI.addControl(Control::Type::Label, "Status:", "Stop", Control::Color::Turquoise); uint16_t select1 - = ESPUI.addControl(ControlType::Select, "Select:", "", ControlColor::Alizarin, tab1, &selectExample); - ESPUI.addControl(ControlType::Option, "Option1", "Opt1", ControlColor::Alizarin, select1); - ESPUI.addControl(ControlType::Option, "Option2", "Opt2", ControlColor::Alizarin, select1); - ESPUI.addControl(ControlType::Option, "Option3", "Opt3", ControlColor::Alizarin, select1); + = ESPUI.addControl(Control::Type::Select, "Select:", "", Control::Color::Alizarin, tab1, &selectExample); + ESPUI.addControl(Control::Type::Option, "Option1", "Opt1", Control::Color::Alizarin, select1); + ESPUI.addControl(Control::Type::Option, "Option2", "Opt2", Control::Color::Alizarin, select1); + ESPUI.addControl(Control::Type::Option, "Option3", "Opt3", Control::Color::Alizarin, select1); - ESPUI.addControl(ControlType::Text, "Text Test:", "a Text Field", ControlColor::Alizarin, tab1, &textCall); + ESPUI.addControl(Control::Type::Text, "Text Test:", "a Text Field", Control::Color::Alizarin, tab1, &textCall); // tabbed controls - ESPUI.addControl(ControlType::Label, "Millis:", "0", ControlColor::Emerald, tab1); + ESPUI.addControl(Control::Type::Label, "Millis:", "0", Control::Color::Emerald, tab1); button1 = ESPUI.addControl( - ControlType::Button, "Push Button", "Press", ControlColor::Peterriver, tab1, &buttonCallback); - ESPUI.addControl(ControlType::Button, "Other Button", "Press", ControlColor::Wetasphalt, tab1, &buttonExample, (void*)19); - ESPUI.addControl(ControlType::PadWithCenter, "Pad with center", "", ControlColor::Sunflower, tab2, &padExample); - ESPUI.addControl(ControlType::Pad, "Pad without center", "", ControlColor::Carrot, tab3, &padExample); - switchOne = ESPUI.addControl(ControlType::Switcher, "Switch one", "", ControlColor::Alizarin, tab3, &switchExample); - ESPUI.addControl(ControlType::Switcher, "Switch two", "", ControlColor::None, tab3, &otherSwitchExample); - ESPUI.addControl(ControlType::Slider, "Slider one", "30", ControlColor::Alizarin, tab1, &slider); - ESPUI.addControl(ControlType::Slider, "Slider two", "100", ControlColor::Alizarin, tab3, &slider); - ESPUI.addControl(ControlType::Number, "Number:", "50", ControlColor::Alizarin, tab3, &numberCall); + Control::Type::Button, "Push Button", "Press", Control::Color::Peterriver, tab1, &buttonCallback); + ESPUI.addControl(Control::Type::Button, "Other Button", "Press", Control::Color::Wetasphalt, tab1, &buttonExample, (void*)19); + ESPUI.addControl(Control::Type::PadWithCenter, "Pad with center", "", Control::Color::Sunflower, tab2, &padExample); + ESPUI.addControl(Control::Type::Pad, "Pad without center", "", Control::Color::Carrot, tab3, &padExample); + switchOne = ESPUI.addControl(Control::Type::Switcher, "Switch one", "", Control::Color::Alizarin, tab3, &switchExample); + ESPUI.addControl(Control::Type::Switcher, "Switch two", "", Control::Color::None, tab3, &otherSwitchExample); + ESPUI.addControl(Control::Type::Slider, "Slider one", "30", Control::Color::Alizarin, tab1, &slider); + ESPUI.addControl(Control::Type::Slider, "Slider two", "100", Control::Color::Alizarin, tab3, &slider); + ESPUI.addControl(Control::Type::Number, "Number:", "50", Control::Color::Alizarin, tab3, &numberCall); /* * .begin loads and serves all files from PROGMEM directly. diff --git a/pio_examples/gui/platformio.ini b/pio_examples/gui/platformio.ini index 5c8dc03..9e39669 100644 --- a/pio_examples/gui/platformio.ini +++ b/pio_examples/gui/platformio.ini @@ -17,10 +17,8 @@ framework = arduino board_build.filesystem = littlefs lib_extra_dirs = ../../ lib_deps = -; bblanchon/ArduinoJson @ ^6.18.5 bblanchon/ArduinoJson @ ^7.0.4 https://github.com/bmedici/ESPAsyncWebServer ; Use a fork of the library that has a bugfix for the compile.... https://github.com/esphome/ESPAsyncWebServer/pull/17 - lib_ignore = ESP Async WebServer ; force the use of the esphome version AsyncTCP ; force the use of the esphome version @@ -36,6 +34,8 @@ board = nodemcuv2 upload_port = COM8 monitor_port = COM8 monitor_speed = 115200 +build_flags = +; -D DEBUG_ESPUI [env:esp32] platform = espressif32 @@ -44,10 +44,9 @@ monitor_filters = esp32_exception_decoder board_build.flash_mode = dout build_flags = ; -D DEBUG_ESPUI - lib_deps = ${env.lib_deps} me-no-dev/AsyncTCP -upload_port = COM6 -monitor_port = COM6 +upload_port = COM10 +monitor_port = COM10 monitor_speed = 115200 diff --git a/pio_examples/gui/src/gui.ino b/pio_examples/gui/src/gui.ino index c479db6..39c805f 100644 --- a/pio_examples/gui/src/gui.ino +++ b/pio_examples/gui/src/gui.ino @@ -37,7 +37,7 @@ void numberCall(Control* sender, int type) void textCall(Control* sender, int type) { Serial.print("Text: ID: "); - Serial.print(sender->id); + Serial.print(sender->GetId()); Serial.print(", Value: "); Serial.println(sender->value); } @@ -45,7 +45,7 @@ void textCall(Control* sender, int type) void slider(Control* sender, int type) { Serial.print("Slider: ID: "); - Serial.print(sender->id); + Serial.print(sender->GetId()); Serial.print(", Value: "); Serial.println(sender->value); // Like all Control Values in ESPUI slider values are Strings. To use them as int simply do this: @@ -130,7 +130,7 @@ void padExample(Control* sender, int value) } Serial.print(" "); - Serial.println(sender->id); + Serial.println(sender->GetId()); } void switchExample(Control* sender, int value) @@ -147,7 +147,7 @@ void switchExample(Control* sender, int value) } Serial.print(" "); - Serial.println(sender->id); + Serial.println(sender->GetId()); } void otherSwitchExample(Control* sender, int value) @@ -164,7 +164,7 @@ void otherSwitchExample(Control* sender, int value) } Serial.print(" "); - Serial.println(sender->id); + Serial.println(sender->GetId()); } void setup(void) @@ -172,6 +172,7 @@ void setup(void) ESPUI.setVerbosity(Verbosity::VerboseJSON); Serial.begin(115200); + memset(HugeText, 0x0, sizeof(HugeText)); memset(HugeText, 'a', sizeof(HugeText)-1); @@ -237,26 +238,26 @@ void setup(void) Serial.print("IP address: "); Serial.println(WiFi.getMode() == WIFI_AP ? WiFi.softAPIP() : WiFi.localIP()); - statusLabelId = ESPUI.label("Status:", ControlColor::Turquoise, "Stop"); - millisLabelId = ESPUI.label("Millis:", ControlColor::Emerald, "0"); - ESPUI.button("Push Button", &buttonCallback, ControlColor::Peterriver, "Press"); - ESPUI.button("Other Button", &buttonExample, ControlColor::Wetasphalt, "Press", (void*)19); - ESPUI.padWithCenter("Pad with center", &padExample, ControlColor::Sunflower); - ESPUI.pad("Pad without center", &padExample, ControlColor::Carrot); - testSwitchId = ESPUI.switcher("Switch one", &switchExample, ControlColor::Alizarin, false); - ESPUI.switcher("Switch two", &otherSwitchExample, ControlColor::None, true); - ESPUI.slider("Slider one", &slider, ControlColor::Alizarin, 30, 0, 30); - ESPUI.slider("Slider two", &slider, ControlColor::None, 100); - ESPUI.text("Text Test:", &textCall, ControlColor::Alizarin, "a Text Field"); + statusLabelId = ESPUI.label("Status:", Control::Color::Turquoise, "Stop"); + millisLabelId = ESPUI.label("Millis:",Control::Color::Emerald, "0"); + ESPUI.button("Push Button", &buttonCallback,Control::Color::Peterriver, "Press"); + ESPUI.button("Other Button", &buttonExample,Control::Color::Wetasphalt, "Press", (void*)19); + ESPUI.padWithCenter("Pad with center", &padExample,Control::Color::Sunflower); + ESPUI.pad("Pad without center", &padExample,Control::Color::Carrot); + testSwitchId = ESPUI.switcher("Switch one", &switchExample,Control::Color::Alizarin, false); + ESPUI.switcher("Switch two", &otherSwitchExample,Control::Color::None, true); + ESPUI.slider("Slider one", &slider,Control::Color::Alizarin, 30, 0, 30); + ESPUI.slider("Slider two", &slider,Control::Color::None, 100); + ESPUI.text("Text Test:", &textCall,Control::Color::Alizarin, "a Text Field"); - ESPUI.text("Huge Text Test:", &textCall, ControlColor::Alizarin, HugeText); + ESPUI.text("Huge Text Test:", &textCall,Control::Color::Alizarin, HugeText); - ESPUI.number("Numbertest", &numberCall, ControlColor::Alizarin, 5, 0, 10); + ESPUI.number("Numbertest", &numberCall,Control::Color::Alizarin, 5, 0, 10); - fileDisplayId = ESPUI.fileDisplay("Filetest", ControlColor::Turquoise, DisplayTestFileName); + fileDisplayId = ESPUI.fileDisplay("Filetest",Control::Color::Turquoise, DisplayTestFileName); #ifdef TEST_GRAPH - graphId = ESPUI.graph("Graph Test", ControlColor::Wetasphalt); + graphId = ESPUI.graph("Graph Test",Control::Color::Wetasphalt); #endif // def TEST_GRAPH /* diff --git a/src/ESPUI.cpp b/src/ESPUI.cpp index 8852750..0713b52 100644 --- a/src/ESPUI.cpp +++ b/src/ESPUI.cpp @@ -16,6 +16,7 @@ #if ESP8266 #include #endif +#include "ESPUIcontrolMgr.h" static String heapInfo(const __FlashStringHelper* mode) { @@ -429,7 +430,7 @@ void ESPUIClass::onWsEvent( AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len) { // Serial.println(String("ESPUIClass::OnWsEvent: type: ") + String(type)); - RemoveToBeDeletedControls(); + ESPUIcontrolMgr.RemoveToBeDeletedControls(); if (WS_EVT_DISCONNECT == type) { @@ -453,7 +454,7 @@ void ESPUIClass::onWsEvent( { ws->cleanupClients(); } - + if (MapOfClients.end() == MapOfClients.find(client->id())) { // Serial.println("ESPUIClass::OnWsEvent:Create new client."); @@ -470,249 +471,129 @@ void ESPUIClass::onWsEvent( return; } -uint16_t ESPUIClass::addControl(ControlType type, const char* label) +Control::ControlId_t ESPUIClass::addControl(Control::Type type, const char* label) { - return addControl(type, label, String("")); + return addControl(type, label, emptyString); } -uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value) +Control::ControlId_t ESPUIClass::addControl(Control::Type type, const char* label, const String& value) { - return addControl(type, label, value, ControlColor::Turquoise); + return addControl(type, label, value, Control::Color::Turquoise); } -uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value, ControlColor color) +Control::ControlId_t ESPUIClass::addControl(Control::Type type, const char* label, const String& value, Control::Color color) { return addControl(type, label, value, color, Control::noParent); } -uint16_t ESPUIClass::addControl( - ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl) +Control::ControlId_t ESPUIClass::addControl(Control::Type type, +const char* label, const String& value, Control::Color color, Control::ControlId_t parentControlId) { - return addControl(type, label, value, color, parentControl, new Control(type, label, nullptr, value, color, true, parentControl)); -} - -uint16_t ESPUIClass::addControl(ControlType type, const char* label, const String& value, ControlColor color, - uint16_t parentControl, std::function callback) -{ - uint16_t id = addControl(type, label, value, color, parentControl); - // set the original style callback - getControl(id)->callback = callback; - return id; + return addControl(type, label, value, color, parentControlId, (std::function)nullptr); } -uint16_t ESPUIClass::addControl( - ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, Control* control) +Control::ControlId_t ESPUIClass::addControl(Control::Type type, + const char* label, const String& value, Control::Color color, + Control::ControlId_t parentControlId, std::function callback) { -#ifdef ESP32 - xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); -#endif // def ESP32 - - if (controls == nullptr) - { - controls = control; - } - else - { - Control* iterator = controls; - - while (iterator->next != nullptr) - { - iterator = iterator->next; - } - - iterator->next = control; - } - - controlCount++; - -#ifdef ESP32 - xSemaphoreGive(ControlsSemaphore); -#endif // def ESP32 - + Control::ControlId_t id = ESPUIcontrolMgr.addControl(type, label, value, color, parentControlId, true, callback); NotifyClients(ClientUpdateType_t::RebuildNeeded); - - return control->id; + return id; } -bool ESPUIClass::removeControl(uint16_t id, bool force_rebuild_ui) +bool ESPUIClass::removeControl(Control::ControlId_t id, bool force_rebuild_ui) { - bool Response = false; - - Control* control = getControl(id); - if (control) + bool Response = ESPUIcontrolMgr.removeControl(id); + if (force_rebuild_ui) { - Response = true; - control->DeleteControl(); - controlCount--; - - if (force_rebuild_ui) - { - jsonReload(); - } - else - { - NotifyClients(ClientUpdateType_t::RebuildNeeded); - } + ESPUI.jsonReload(); } -#ifdef DEBUG_ESPUI else { - // Serial.println(String("Could not Remove Control ") + String(id)); + ESPUI.NotifyClients(ClientUpdateType_t::RebuildNeeded); } -#endif // def DEBUG_ESPUI - return Response; -} - -void ESPUIClass::RemoveToBeDeletedControls() -{ -#ifdef ESP32 - xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); -#endif // def ESP32 - - Control* PreviousControl = nullptr; - Control* CurrentControl = controls; - - while (nullptr != CurrentControl) - { - Control* NextControl = CurrentControl->next; - if (CurrentControl->ToBeDeleted()) - { - if (CurrentControl == controls) - { - // this is the root control - controls = NextControl; - } - else - { - PreviousControl->next = NextControl; - } - delete CurrentControl; - CurrentControl = NextControl; - } - else - { - PreviousControl = CurrentControl; - CurrentControl = NextControl; - } - } -#ifdef ESP32 - xSemaphoreGive(ControlsSemaphore); -#endif // def ESP32 -} +} // removeControl -uint16_t ESPUIClass::label(const char* label, ControlColor color, const String& value) +Control::ControlId_t ESPUIClass::label(const char* label, Control::Color color, const String& value) { - return addControl(ControlType::Label, label, value, color); + return addControl(Control::Type::Label, label, value, color); } -uint16_t ESPUIClass::graph(const char* label, ControlColor color) +Control::ControlId_t ESPUIClass::graph(const char* label, Control::Color color) { - return addControl(ControlType::Graph, label, "", color); + return addControl(Control::Type::Graph, label, "", color); } -uint16_t ESPUIClass::slider( - const char* label, std::function callback, ControlColor color, int value, int min, int max) +Control::ControlId_t ESPUIClass::slider( + const char* label, std::function callback, Control::Color color, int value, int min, int max) { - uint16_t sliderId - = addControl(ControlType::Slider, label, String(value), color, Control::noParent, callback); - addControl(ControlType::Min, label, String(min), ControlColor::None, sliderId); - addControl(ControlType::Max, label, String(max), ControlColor::None, sliderId); + Control::ControlId_t sliderId + = addControl(Control::Type::Slider, label, String(value), color, Control::noParent, callback); + addControl(Control::Type::Min, label, String(min), Control::Color::None, sliderId); + addControl(Control::Type::Max, label, String(max), Control::Color::None, sliderId); return sliderId; } -uint16_t ESPUIClass::button(const char* label, std::function callback, ControlColor color, const String& value) +Control::ControlId_t ESPUIClass::button(const char* label, std::function callback, Control::Color color, const String& value) { - return addControl(ControlType::Button, label, value, color, Control::noParent, callback); + return addControl(Control::Type::Button, label, value, color, Control::noParent, callback); } -uint16_t ESPUIClass::switcher(const char* label, std::function callback, ControlColor color, bool startState) +Control::ControlId_t ESPUIClass::switcher(const char* label, std::function callback, Control::Color color, bool startState) { - return addControl(ControlType::Switcher, label, startState ? "1" : "0", color, Control::noParent, callback); + return addControl(Control::Type::Switcher, label, startState ? "1" : "0", color, Control::noParent, callback); } -uint16_t ESPUIClass::pad(const char* label, std::function callback, ControlColor color) +Control::ControlId_t ESPUIClass::pad(const char* label, std::function callback, Control::Color color) { - return addControl(ControlType::Pad, label, "", color, Control::noParent, callback); + return addControl(Control::Type::Pad, label, "", color, Control::noParent, callback); } -uint16_t ESPUIClass::padWithCenter(const char* label, std::function callback, ControlColor color) +Control::ControlId_t ESPUIClass::padWithCenter(const char* label, std::function callback, Control::Color color) { - return addControl(ControlType::PadWithCenter, label, "", color, Control::noParent, callback); + return addControl(Control::Type::PadWithCenter, label, "", color, Control::noParent, callback); } -uint16_t ESPUIClass::number( - const char* label, std::function callback, ControlColor color, int number, int min, int max) +Control::ControlId_t ESPUIClass::number( + const char* label, std::function callback, Control::Color color, int number, int min, int max) { - uint16_t numberId = addControl(ControlType::Number, label, String(number), color, Control::noParent, callback); - addControl(ControlType::Min, label, String(min), ControlColor::None, numberId); - addControl(ControlType::Max, label, String(max), ControlColor::None, numberId); + Control::ControlId_t numberId = addControl(Control::Type::Number, label, String(number), color, Control::noParent, callback); + addControl(Control::Type::Min, label, String(min), Control::Color::None, numberId); + addControl(Control::Type::Max, label, String(max), Control::Color::None, numberId); return numberId; } -uint16_t ESPUIClass::gauge(const char* label, ControlColor color, int number, int min, int max) +Control::ControlId_t ESPUIClass::gauge(const char* label, Control::Color color, int number, int min, int max) { - uint16_t numberId = addControl(ControlType::Gauge, label, String(number), color, Control::noParent); - addControl(ControlType::Min, label, String(min), ControlColor::None, numberId); - addControl(ControlType::Max, label, String(max), ControlColor::None, numberId); + Control::ControlId_t numberId = addControl(Control::Type::Gauge, label, String(number), color, Control::noParent); + addControl(Control::Type::Min, label, String(min), Control::Color::None, numberId); + addControl(Control::Type::Max, label, String(max), Control::Color::None, numberId); return numberId; } -uint16_t ESPUIClass::separator(const char* label) +Control::ControlId_t ESPUIClass::separator(const char* label) { - return addControl(ControlType::Separator, label, "", ControlColor::Alizarin, Control::noParent, nullptr); + return addControl(Control::Type::Separator, label, "", Control::Color::Alizarin, Control::noParent, nullptr); } -uint16_t ESPUIClass::fileDisplay(const char* label, ControlColor color, String filename) +Control::ControlId_t ESPUIClass::fileDisplay(const char* label, Control::Color color, String filename) { - return addControl(ControlType::FileDisplay, label, filename, color, Control::noParent); + return addControl(Control::Type::FileDisplay, label, filename, color, Control::noParent); } -uint16_t ESPUIClass::accelerometer(const char* label, std::function callback, ControlColor color) +Control::ControlId_t ESPUIClass::accelerometer(const char* label, std::function callback, Control::Color color) { - return addControl(ControlType::Accel, label, "", color, Control::noParent, callback); + return addControl(Control::Type::Accel, label, "", color, Control::noParent, callback); } -uint16_t ESPUIClass::text(const char* label, std::function callback, ControlColor color, const String& value) +Control::ControlId_t ESPUIClass::text(const char* label, std::function callback, Control::Color color, const String& value) { - return addControl(ControlType::Text, label, value, color, Control::noParent, callback); + return addControl(Control::Type::Text, label, value, color, Control::noParent, callback); } -Control* ESPUIClass::getControl(uint16_t id) -{ -#ifdef ESP32 - xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); - Control* Response = getControlNoLock(id); - xSemaphoreGive(ControlsSemaphore); - return Response; -#else - return getControlNoLock(id); -#endif // !def ESP32 -} - -// WARNING: Anytime you walk the chain of controllers, the protection semaphore -// MUST be locked. This function assumes that the semaphore is locked -// at the time it is called. Make sure YOU locked it :) -Control* ESPUIClass::getControlNoLock(uint16_t id) -{ - Control* Response = nullptr; - Control* control = controls; - - while (nullptr != control) - { - if (control->id == id) - { - if (!control->ToBeDeleted()) - { - Response = control; - } - break; - } - control = control->next; - } - - return Response; -} +Control* ESPUIClass::getControl(Control::ControlId_t id) {return ESPUIcontrolMgr.getControl(id);} +Control* ESPUIClass::getControlNoLock(Control::ControlId_t id) {return ESPUIcontrolMgr.getControlNoLock(id);} void ESPUIClass::updateControl(Control* control, int) { @@ -735,7 +616,7 @@ uint32_t ESPUIClass::GetNextControlChangeId() return ++ControlChangeID; } -void ESPUIClass::setPanelStyle(uint16_t id, const String& style, int clientId) +void ESPUIClass::setPanelStyle(Control::ControlId_t id, const String& style, int clientId) { Control* control = getControl(id); if (control) @@ -745,7 +626,7 @@ void ESPUIClass::setPanelStyle(uint16_t id, const String& style, int clientId) } } -void ESPUIClass::setElementStyle(uint16_t id, const String& style, int clientId) +void ESPUIClass::setElementStyle(Control::ControlId_t id, const String& style, int clientId) { Control* control = getControl(id); if (control) @@ -755,7 +636,7 @@ void ESPUIClass::setElementStyle(uint16_t id, const String& style, int clientId) } } -void ESPUIClass::setInputType(uint16_t id, const String& type, int clientId) +void ESPUIClass::setInputType(Control::ControlId_t id, const String& type, int clientId) { Control* control = getControl(id); if (control) @@ -765,7 +646,7 @@ void ESPUIClass::setInputType(uint16_t id, const String& type, int clientId) } } -void ESPUIClass::setPanelWide(uint16_t id, bool wide) +void ESPUIClass::setPanelWide(Control::ControlId_t id, bool wide) { Control* control = getControl(id); if (control) @@ -774,7 +655,7 @@ void ESPUIClass::setPanelWide(uint16_t id, bool wide) } } -void ESPUIClass::setEnabled(uint16_t id, bool enabled, int clientId) +void ESPUIClass::setEnabled(Control::ControlId_t id, bool enabled, int clientId) { Control* control = getControl(id); if (control) @@ -785,7 +666,7 @@ void ESPUIClass::setEnabled(uint16_t id, bool enabled, int clientId) } } -void ESPUIClass::setVertical(uint16_t id, bool vert) +void ESPUIClass::setVertical(Control::ControlId_t id, bool vert) { Control* control = getControl(id); if (control) @@ -794,7 +675,7 @@ void ESPUIClass::setVertical(uint16_t id, bool vert) } } -void ESPUIClass::updateControl(uint16_t id, int clientId) +void ESPUIClass::updateControl(Control::ControlId_t id, int clientId) { Control* control = getControl(id); @@ -823,7 +704,7 @@ void ESPUIClass::updateControlValue(Control* control, const String& value, int c updateControl(control, clientId); } -void ESPUIClass::updateControlValue(uint16_t id, const String& value, int clientId) +void ESPUIClass::updateControlValue(Control::ControlId_t id, const String& value, int clientId) { Control* control = getControl(id); @@ -841,7 +722,7 @@ void ESPUIClass::updateControlValue(uint16_t id, const String& value, int client updateControlValue(control, value, clientId); } -void ESPUIClass::updateControlLabel(uint16_t id, const char* value, int clientId) +void ESPUIClass::updateControlLabel(Control::ControlId_t id, const char* value, int clientId) { updateControlLabel(getControl(id), value, clientId); } @@ -862,7 +743,7 @@ void ESPUIClass::updateControlLabel(Control* control, const char* value, int cli updateControl(control, clientId); } -void ESPUIClass::updateVisibility(uint16_t id, bool visibility, int clientId) +void ESPUIClass::updateVisibility(Control::ControlId_t id, bool visibility, int clientId) { Control* control = getControl(id); if (control) @@ -872,57 +753,57 @@ void ESPUIClass::updateVisibility(uint16_t id, bool visibility, int clientId) } } -void ESPUIClass::print(uint16_t id, const String& value) +void ESPUIClass::print(Control::ControlId_t id, const String& value) { updateControlValue(id, value); } -void ESPUIClass::updateLabel(uint16_t id, const String& value) +void ESPUIClass::updateLabel(Control::ControlId_t id, const String& value) { updateControlValue(id, value); } -void ESPUIClass::updateButton(uint16_t id, const String& value) +void ESPUIClass::updateButton(Control::ControlId_t id, const String& value) { updateControlValue(id, value); } -void ESPUIClass::updateSlider(uint16_t id, int nValue, int clientId) +void ESPUIClass::updateSlider(Control::ControlId_t id, int nValue, int clientId) { updateControlValue(id, String(nValue), clientId); } -void ESPUIClass::updateSwitcher(uint16_t id, bool nValue, int clientId) +void ESPUIClass::updateSwitcher(Control::ControlId_t id, bool nValue, int clientId) { updateControlValue(id, String(nValue ? "1" : "0"), clientId); } -void ESPUIClass::updateNumber(uint16_t id, int number, int clientId) +void ESPUIClass::updateNumber(Control::ControlId_t id, int number, int clientId) { updateControlValue(id, String(number), clientId); } -void ESPUIClass::updateText(uint16_t id, const String& text, int clientId) +void ESPUIClass::updateText(Control::ControlId_t id, const String& text, int clientId) { updateControlValue(id, text, clientId); } -void ESPUIClass::updateSelect(uint16_t id, const String& text, int clientId) +void ESPUIClass::updateSelect(Control::ControlId_t id, const String& text, int clientId) { updateControlValue(id, text, clientId); } -void ESPUIClass::updateGauge(uint16_t id, int number, int clientId) +void ESPUIClass::updateGauge(Control::ControlId_t id, int number, int clientId) { updateControlValue(id, String(number), clientId); } -void ESPUIClass::updateTime(uint16_t id, int clientId) +void ESPUIClass::updateTime(Control::ControlId_t id, int clientId) { updateControl(id, clientId); } -void ESPUIClass::clearGraph(uint16_t id, int clientId) +void ESPUIClass::clearGraph(Control::ControlId_t id, int clientId) { do // once { @@ -935,16 +816,16 @@ void ESPUIClass::clearGraph(uint16_t id, int clientId) AllocateJsonDocument(document, jsonUpdateDocumentSize); JsonObject root = document.to(); - root[F("type")] = (int)ControlType::Graph + UpdateOffset; + root[F("type")] = (int)Control::Type::Graph + Control::Type::UpdateOffset; root[F("value")] = 0; - root[F("id")] = control->id; + root[F("id")] = control->GetId(); SendJsonDocToWebSocket(document, clientId); } while (false); } -void ESPUIClass::addGraphPoint(uint16_t id, int nValue, int clientId) +void ESPUIClass::addGraphPoint(Control::ControlId_t id, int nValue, int clientId) { do // once { @@ -957,9 +838,9 @@ void ESPUIClass::addGraphPoint(uint16_t id, int nValue, int clientId) AllocateJsonDocument(document, jsonUpdateDocumentSize); JsonObject root = document.to(); - root[F("type")] = (int)ControlType::GraphPoint; + root[F("type")] = (int)Control::Type::GraphPoint; root[F("value")] = nValue; - root[F("id")] = control->id; + root[F("id")] = control->GetId(); SendJsonDocToWebSocket(document, clientId); diff --git a/src/ESPUI.h b/src/ESPUI.h index 0e7bcc4..2fa8bd7 100644 --- a/src/ESPUI.h +++ b/src/ESPUI.h @@ -49,6 +49,8 @@ #endif +#include + #define FILE_WRITING "w" // Message Types (and control types) @@ -102,10 +104,6 @@ class ESPUIClass public: ESPUIClass() { -#ifdef ESP32 - ControlsSemaphore = xSemaphoreCreateMutex(); - xSemaphoreGive(ControlsSemaphore); -#endif // def ESP32 } unsigned int jsonUpdateDocumentSize = 2000; #ifdef ESP8266 @@ -132,117 +130,116 @@ class ESPUIClass void list(); // Lists LITTLEFS directory void writeFile(const char* path, const char* data); - uint16_t addControl(ControlType type, const char* label); - uint16_t addControl(ControlType type, const char* label, const String& value); - uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color); - uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl); - uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, std::function callback); + Control::ControlId_t addControl(Control::Type type, const char* label); + Control::ControlId_t addControl(Control::Type type, const char* label, const String& value); + Control::ControlId_t addControl(Control::Type type, const char* label, const String& value, Control::Color color); + Control::ControlId_t addControl(Control::Type type, const char* label, const String& value, Control::Color color, Control::ControlId_t parentControlId); + Control::ControlId_t addControl(Control::Type type, const char* label, const String& value, Control::Color color, Control::ControlId_t parentControlId, std::function callback); - bool removeControl(uint16_t id, bool force_rebuild_ui = false); + bool removeControl(Control::ControlId_t id, bool force_rebuild_ui = false); // create Elements // Create Event Button - uint16_t button(const char* label, std::function callback, ControlColor color, const String& value = ""); - uint16_t switcher(const char* label, std::function callback, ControlColor color, bool startState = false); // Create Toggle Button - uint16_t pad(const char* label, std::function callback, ControlColor color); // Create Pad Control - uint16_t padWithCenter(const char* label, std::function callback, ControlColor color); // Create Pad Control with Centerbutton - uint16_t slider(const char* label, std::function callback, ControlColor color, int value, int min = 0, int max = 100); // Create Slider Control - uint16_t number(const char* label, std::function callback, ControlColor color, int value, int min = 0, int max = 100); // Create a Number Input Control - uint16_t text(const char* label, std::function callback, ControlColor color, const String& value = ""); // Create a Text Input Control + Control::ControlId_t button(const char* label, std::function callback, Control::Color color, const String& value = ""); + Control::ControlId_t switcher(const char* label, std::function callback, Control::Color color, bool startState = false); // Create Toggle Button + Control::ControlId_t pad(const char* label, std::function callback, Control::Color color); // Create Pad Control + Control::ControlId_t padWithCenter(const char* label, std::function callback, Control::Color color); // Create Pad Control with Centerbutton + Control::ControlId_t slider(const char* label, std::function callback, Control::Color color, int value, int min = 0, int max = 100); // Create Slider Control + Control::ControlId_t number(const char* label, std::function callback, Control::Color color, int value, int min = 0, int max = 100); // Create a Number Input Control + Control::ControlId_t text(const char* label, std::function callback, Control::Color color, const String& value = ""); // Create a Text Input Control // Output only - uint16_t label(const char* label, ControlColor color, + Control::ControlId_t label(const char* label, Control::Color color, const String& value = ""); // Create Label - uint16_t graph(const char* label, ControlColor color); // Create Graph display - uint16_t gauge(const char* label, ControlColor color, int value, int min = 0, + Control::ControlId_t graph(const char* label, Control::Color color); // Create Graph display + Control::ControlId_t gauge(const char* label, Control::Color color, int value, int min = 0, int max = 100); // Create Gauge display - uint16_t separator(const char* label); //Create separator - uint16_t fileDisplay(const char* label, ControlColor color, String filename); + Control::ControlId_t separator(const char* label); //Create separator + Control::ControlId_t fileDisplay(const char* label, Control::Color color, String filename); // Input only - uint16_t accelerometer(const char* label, std::function callback, ControlColor color); + Control::ControlId_t accelerometer(const char* label, std::function callback, Control::Color color); // Update Elements - Control* getControl(uint16_t id); - Control* getControlNoLock(uint16_t id); + Control* getControl(Control::ControlId_t id); + Control* getControlNoLock(Control::ControlId_t id); // Update Elements - void updateControlValue(uint16_t id, const String& value, int clientId = -1); + void updateControlValue(Control::ControlId_t id, const String& value, int clientId = -1); void updateControlValue(Control* control, const String& value, int clientId = -1); - void updateControlLabel(uint16_t control, const char * value, int clientId = -1); + void updateControlLabel(Control::ControlId_t control, const char * value, int clientId = -1); void updateControlLabel(Control* control, const char * value, int clientId = -1); - void updateControl(uint16_t id, int clientId = -1); + void updateControl(Control::ControlId_t id, int clientId = -1); void updateControl(Control* control, int clientId = -1); - void print(uint16_t id, const String& value); - void updateLabel(uint16_t id, const String& value); - void updateButton(uint16_t id, const String& value); - void updateSwitcher(uint16_t id, bool nValue, int clientId = -1); - void updateSlider(uint16_t id, int nValue, int clientId = -1); - void updateNumber(uint16_t id, int nValue, int clientId = -1); - void updateText(uint16_t id, const String& nValue, int clientId = -1); - void updateSelect(uint16_t id, const String& nValue, int clientId = -1); - void updateGauge(uint16_t id, int number, int clientId); - void updateTime(uint16_t id, int clientId = -1); + void print(Control::ControlId_t id, const String& value); + void updateLabel(Control::ControlId_t id, const String& value); + void updateButton(Control::ControlId_t id, const String& value); + void updateSwitcher(Control::ControlId_t id, bool nValue, int clientId = -1); + void updateSlider(Control::ControlId_t id, int nValue, int clientId = -1); + void updateNumber(Control::ControlId_t id, int nValue, int clientId = -1); + void updateText(Control::ControlId_t id, const String& nValue, int clientId = -1); + void updateSelect(Control::ControlId_t id, const String& nValue, int clientId = -1); + void updateGauge(Control::ControlId_t id, int number, int clientId); + void updateTime(Control::ControlId_t id, int clientId = -1); - void clearGraph(uint16_t id, int clientId = -1); - void addGraphPoint(uint16_t id, int nValue, int clientId = -1); + void clearGraph(Control::ControlId_t id, int clientId = -1); + void addGraphPoint(Control::ControlId_t id, int nValue, int clientId = -1); - void setPanelStyle(uint16_t id, const String& style, int clientId = -1); - void setElementStyle(uint16_t id, const String& style, int clientId = -1); - void setInputType(uint16_t id, const String& type, int clientId = -1); + void setPanelStyle(Control::ControlId_t id, const String& style, int clientId = -1); + void setElementStyle(Control::ControlId_t id, const String& style, int clientId = -1); + void setInputType(Control::ControlId_t id, const String& type, int clientId = -1); - void setPanelWide(uint16_t id, bool wide); - void setVertical(uint16_t id, bool vert = true); - void setEnabled(uint16_t id, bool enabled = true, int clientId = -1); + void setPanelWide(Control::ControlId_t id, bool wide); + void setVertical(Control::ControlId_t id, bool vert = true); + void setEnabled(Control::ControlId_t id, bool enabled = true, int clientId = -1); - void updateVisibility(uint16_t id, bool visibility, int clientId = -1); + void updateVisibility(Control::ControlId_t id, bool visibility, int clientId = -1); // Variables const char* ui_title = "ESPUI"; // Store UI Title and Header Name - Control* controls = nullptr; void jsonReload(); void jsonDom(uint16_t startidx, AsyncWebSocketClient* client = nullptr, bool Updating = false); Verbosity verbosity = Verbosity::Quiet; uint32_t GetNextControlChangeId(); // emulate former extended callback API by using an intermediate lambda (no deprecation) - uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, std::function callback, void* userData) + Control::ControlId_t addControl(Control::Type type, const char* label, const String& value, Control::Color color, Control::ControlId_t parentControl, std::function callback, void* userData) { return addControl(type, label, value, color, parentControl, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }); } - uint16_t button(const char* label, std::function callback, ControlColor color, const String& value, void* userData) + Control::ControlId_t button(const char* label, std::function callback, Control::Color color, const String& value, void* userData) { return button(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, value); } - uint16_t switcher(const char* label, std::function callback, ControlColor color, bool startState, void* userData) + Control::ControlId_t switcher(const char* label, std::function callback, Control::Color color, bool startState, void* userData) { return switcher(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, startState); } - uint16_t pad(const char* label, std::function callback, ControlColor color, void* userData) + Control::ControlId_t pad(const char* label, std::function callback, Control::Color color, void* userData) { return pad(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color); } - uint16_t padWithCenter(const char* label, std::function callback, ControlColor color, void* userData) + Control::ControlId_t padWithCenter(const char* label, std::function callback, Control::Color color, void* userData) { return padWithCenter(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color); } - uint16_t slider(const char* label, std::function callback, ControlColor color, int value, int min, int max, void* userData) + Control::ControlId_t slider(const char* label, std::function callback, Control::Color color, int value, int min, int max, void* userData) { return slider(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, value, min, max); } - uint16_t number(const char* label, std::function callback, ControlColor color, int value, int min, int max, void* userData) + Control::ControlId_t number(const char* label, std::function callback, Control::Color color, int value, int min, int max, void* userData) { return number(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color, value, min, max); } - uint16_t text(const char* label, std::function callback, ControlColor color, const String& value, void* userData) + Control::ControlId_t text(const char* label, std::function callback, Control::Color color, const String& value, void* userData) { return text(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); } , color, value); } - uint16_t accelerometer(const char* label, std::function callback, ControlColor color, void* userData) + Control::ControlId_t accelerometer(const char* label, std::function callback, Control::Color color, void* userData) { return accelerometer(label, [callback, userData](Control* sender, int type){ callback(sender, type, userData); }, color); } @@ -264,21 +261,14 @@ class ESPUIClass friend class ESPUIclient; friend class ESPUIcontrol; -#ifdef ESP32 - SemaphoreHandle_t ControlsSemaphore = NULL; -#endif // def ESP32 - - void RemoveToBeDeletedControls(); - AsyncWebServer* server; AsyncWebSocket* ws; const char* basicAuthUsername = nullptr; const char* basicAuthPassword = nullptr; bool basicAuth = true; - uint16_t controlCount = 0; - uint16_t addControl(ControlType type, const char* label, const String& value, ControlColor color, uint16_t parentControl, Control* control); + Control::ControlId_t addControl(Control::Type type, const char* label, const String& value, Control::Color color, Control::ControlId_t parentControl, Control* control); #define ClientUpdateType_t ESPUIclient::ClientUpdateType_t void NotifyClients(ClientUpdateType_t newState); diff --git a/src/ESPUIclient.cpp b/src/ESPUIclient.cpp index 0becacf..a6bf8b9 100644 --- a/src/ESPUIclient.cpp +++ b/src/ESPUIclient.cpp @@ -79,7 +79,7 @@ void ESPUIclient::FillInHeader(JsonDocument& document) document[F("type")] = UI_EXTEND_GUI; document[F("sliderContinuous")] = ESPUI.sliderContinuous; document[F("startindex")] = 0; - document[F("totalcontrols")] = ESPUI.controlCount; + document[F("totalcontrols")] = ESPUIcontrolMgr.GetControlCount(); JsonArray items = AllocateJsonArray(document, F("controls")); JsonObject titleItem = AllocateJsonObject(items); titleItem[F("type")] = (int)UI_TITLE; @@ -253,213 +253,6 @@ bool ESPUIclient::onWsEvent(AwsEventType type, void* arg, uint8_t* data, size_t return Response; } -/* -Prepare a chunk of elements as a single JSON string. If the allowed number of elements is greater than the total -number this will represent the entire UI. More likely, it will represent a small section of the UI to be sent. The -client will acknowledge receipt by requesting the next chunk. - */ -uint32_t ESPUIclient::prepareJSONChunk(uint16_t startindex, - JsonDocument & rootDoc, - bool InUpdateMode, - String FragmentRequestString) -{ -#ifdef ESP32 - xSemaphoreTake(ESPUI.ControlsSemaphore, portMAX_DELAY); -#endif // def ESP32 - - // Serial.println(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode)); - // Serial.println(String("prepareJSONChunk: Start. startindex: ") + String(startindex)); - // Serial.println(String("prepareJSONChunk: Start. FragmentRequestString: '") + FragmentRequestString + "'"); - int elementcount = 0; - uint32_t MaxMarshaledJsonSize = (!InUpdateMode) ? ESPUI.jsonInitialDocumentSize: ESPUI.jsonUpdateDocumentSize; - uint32_t EstimatedUsedMarshaledJsonSize = 0; - - do // once - { - // Follow the list until control points to the startindex'th node - Control* control = ESPUI.controls; - uint32_t currentIndex = 0; - uint32_t DataOffset = 0; - JsonArray items = rootDoc[F("controls")]; - bool SingleControl = false; - - if(!emptyString.equals(FragmentRequestString)) - { - // Serial.println(F("prepareJSONChunk:Fragmentation:Got Header (1)")); - // Serial.println(String("prepareJSONChunk:startindex: ") + String(startindex)); - // Serial.println(String("prepareJSONChunk:currentIndex: ") + String(currentIndex)); - // Serial.println(String("prepareJSONChunk:FragmentRequestString: '") + FragmentRequestString + "'"); - - // this is actually a fragment or directed update request - // parse the string we got from the UI and try to update that specific - // control. - AllocateJsonDocument(FragmentRequest, FragmentRequestString.length() * 3); -/* - ArduinoJson::detail::sizeofObject(N); - if(0 >= FragmentRequest.capacity()) - { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not allocate memory for a fragmentation request. Skipping Response")); - break; - } -*/ - size_t FragmentRequestStartOffset = FragmentRequestString.indexOf("{"); - DeserializationError error = deserializeJson(FragmentRequest, FragmentRequestString.substring(FragmentRequestStartOffset)); - if(DeserializationError::Ok != error) - { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not extract json from the fragment request")); - break; - } - - if(!FragmentRequest.containsKey(F("id"))) - { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a control ID")); - break; - } - uint16_t ControlId = uint16_t(FragmentRequest[F("id")]); - - if(!FragmentRequest.containsKey(F("offset"))) - { - Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a starting offset")); - break; - } - DataOffset = uint16_t(FragmentRequest[F("offset")]); - control = ESPUI.getControlNoLock(ControlId); - if(nullptr == control) - { - Serial.println(String(F("ERROR:prepareJSONChunk:Fragmentation:Requested control: ")) + String(ControlId) + F(" does not exist")); - break; - } - - // Serial.println(F("prepareJSONChunk:Fragmentation:disable the control search operation")); - currentIndex = 1; - startindex = 0; - SingleControl = true; - } - - // find a control to send - while ((startindex > currentIndex) && (nullptr != control)) - { - // only count active controls - if (!control->ToBeDeleted()) - { - if(InUpdateMode) - { - // In update mode we only count the controls that have been updated. - if(control->NeedsSync(CurrentSyncID)) - { - ++currentIndex; - } - } - else - { - // not in update mode. Count all active controls - ++currentIndex; - } - } - control = control->next; - } - - // any controls left to be processed? - if(nullptr == control) - { - // Serial.println("prepareJSONChunk: No controls to process"); - break; - } - - // keep track of the number of elements we have serialised into this - // message. Overflow is detected and handled later in this loop - // and needs an index to the last item added. - while (nullptr != control) - { - // skip deleted controls or controls that have not been updated - if (control->ToBeDeleted() && !SingleControl) - { - // Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id)); - control = control->next; - continue; - } - - if(InUpdateMode && !SingleControl) - { - if(control->NeedsSync(CurrentSyncID)) - { - // dont skip this control - } - else - { - // control has not been updated. Skip it - control = control->next; - continue; - } - } - - // Serial.println(String(F("prepareJSONChunk: MaxMarshaledJsonSize: ")) + String(MaxMarshaledJsonSize)); - // Serial.println(String(F("prepareJSONChunk: Cur EstimatedUsedMarshaledJsonSize: ")) + String(EstimatedUsedMarshaledJsonSize)); - - JsonObject item = AllocateJsonObject(items); - elementcount++; - uint32_t RemainingSpace = (MaxMarshaledJsonSize - EstimatedUsedMarshaledJsonSize) - 100; - // Serial.println(String(F("prepareJSONChunk: RemainingSpace: ")) + String(RemainingSpace)); - uint32_t SpaceUsedByMarshaledControl = 0; - bool ControlIsFragmented = control->MarshalControl(item, - InUpdateMode, - DataOffset, - RemainingSpace, - SpaceUsedByMarshaledControl); - // Serial.println(String(F("prepareJSONChunk: SpaceUsedByMarshaledControl: ")) + String(SpaceUsedByMarshaledControl)); - EstimatedUsedMarshaledJsonSize += SpaceUsedByMarshaledControl; - // Serial.println(String(F("prepareJSONChunk: New EstimatedUsedMarshaledJsonSize: ")) + String(EstimatedUsedMarshaledJsonSize)); - // Serial.println(String(F("prepareJSONChunk: ControlIsFragmented: ")) + String(ControlIsFragmented)); - - // did the control get added to the doc? - if (0 == SpaceUsedByMarshaledControl || - (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0)) - { - // Serial.println( String("prepareJSONChunk: too much data in the message. Remove the last entry")); - if (1 == elementcount) - { - // Serial.println(String(F("prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser.")); - // Serial.println(String(F("ERROR: prepareJSONChunk: value: ")) + control->value); - rootDoc.clear(); - item = AllocateJsonObject(items); - control->MarshalErrorMessage(item); - elementcount = 0; - } - else - { - // Serial.println(String("prepareJSONChunk: Defering control: ") + String(control->id)); - // Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount)); - - items.remove(elementcount); - --elementcount; - } - // exit the loop - control = nullptr; - } - else if ((SingleControl) || - (ControlIsFragmented) || - (MaxMarshaledJsonSize < (EstimatedUsedMarshaledJsonSize + 100))) - { - // Serial.println("prepareJSONChunk: Doc is Full, Fragmented Control or Single Control. exit loop"); - control = nullptr; - } - else - { - // Serial.println("prepareJSONChunk: Next Control"); - control = control->next; - } - } // end while (control != nullptr) - - } while (false); - -#ifdef ESP32 - xSemaphoreGive(ESPUI.ControlsSemaphore); -#endif // def ESP32 - - // Serial.println(String("prepareJSONChunk: END: elementcount: ") + String(elementcount)); - return elementcount; -} - /* Convert & Transfer Arduino elements to JSON elements. This function sends a chunk of JSON describing the controls of the UI, starting from the control at index startidx. @@ -492,7 +285,7 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t Tra break; } - else if ((startidx >= ESPUI.controlCount) && (emptyString.equals(FragmentRequest))) + else if ((startidx >= ESPUIcontrolMgr.GetControlCount()) && (emptyString.equals(FragmentRequest))) { // Serial.println(F("ERROR:ESPUIclient:SendControlsToClient: No more controls to send.")); Response = true; @@ -514,7 +307,7 @@ bool ESPUIclient::SendControlsToClient(uint16_t startidx, ClientUpdateType_t Tra // Serial.println(String("ESPUIclient:SendControlsToClient:type: ") + String((uint32_t)document["type"])); // Serial.println("ESPUIclient:SendControlsToClient: Build Controls."); - if(prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode, FragmentRequest)) + if(ESPUIcontrolMgr.prepareJSONChunk(startidx, document, ClientUpdateType_t::UpdateNeeded == TransferMode, FragmentRequest, CurrentSyncID)) { #if defined(DEBUG_ESPUI) if (ESPUI.verbosity >= Verbosity::VerboseJSON) diff --git a/src/ESPUIclient.h b/src/ESPUIclient.h index b013f5e..34deb14 100644 --- a/src/ESPUIclient.h +++ b/src/ESPUIclient.h @@ -44,7 +44,6 @@ class ESPUIclient bool CanSend(); void FillInHeader(ArduinoJson::JsonDocument& document); - uint32_t prepareJSONChunk(uint16_t startindex, JsonDocument& rootDoc, bool InUpdateMode, String value); bool SendControlsToClient(uint16_t startidx, ClientUpdateType_t TransferMode, String FragmentRequest); bool SendClientNotification(ClientUpdateType_t value); diff --git a/src/ESPUIcontrol.cpp b/src/ESPUIcontrol.cpp index 36647bf..e348553 100644 --- a/src/ESPUIcontrol.cpp +++ b/src/ESPUIcontrol.cpp @@ -1,11 +1,11 @@ #include "ESPUI.h" -static uint16_t idCounter = 0; static const String ControlError = "*** ESPUI ERROR: Could not transfer control ***"; -Control::Control(ControlType type, const char* label, std::function callback, - const String& value, ControlColor color, bool visible, uint16_t parentControl) - : type(type), +Control::Control(Control::ControlId_t id, Control::Type type, const char* label, std::function callback, + const String& value, Control::Color color, bool visible, ControlId_t parentControl) + : id(id), + type(type), label(label), callback(callback), value(value), @@ -14,24 +14,21 @@ Control::Control(ControlType type, const char* label, std::function - if (ControlType::Option == type) + if (Control::Type::Option == type) { Control* ParentControl = ESPUI.getControlNoLock(parentControl); if (nullptr == ParentControl) @@ -189,11 +186,11 @@ bool Control::MarshalControl(JsonObject & _item, void Control::MarshalErrorMessage(JsonObject & item) { item[F("id")] = id; - item[F("type")] = uint32_t(ControlType::Label); + item[F("type")] = uint32_t(Control::Type::Label); item[F("label")] = ControlError.c_str(); item[F("value")] = ControlError; item[F("visible")] = true; - item[F("color")] = (int)ControlColor::Carrot; + item[F("color")] = (int)Control::Color::Carrot; item[F("enabled")] = true; if (parentControl != Control::noParent) @@ -225,7 +222,7 @@ void Control::onWsEvent(String & cmd, String& data) SendCallback(B_DOWN); break; } - + if (cmd.equals(F("bup"))) { SendCallback(B_UP); @@ -237,7 +234,7 @@ void Control::onWsEvent(String & cmd, String& data) SendCallback(P_FOR_DOWN); break; } - + if (cmd.equals(F("pfup"))) { SendCallback(P_FOR_UP); @@ -249,7 +246,6 @@ void Control::onWsEvent(String & cmd, String& data) SendCallback(P_LEFT_DOWN); break; } - else if (cmd.equals(F("plup"))) { SendCallback(P_LEFT_UP); diff --git a/src/ESPUIcontrol.h b/src/ESPUIcontrol.h index 1701ae0..90ef66b 100644 --- a/src/ESPUIcontrol.h +++ b/src/ESPUIcontrol.h @@ -4,79 +4,79 @@ #include #include -enum ControlType : uint8_t -{ - // fixed Controls - Title = 0, - - // updatable Controls - Pad, - PadWithCenter, - Button, - Label, - Switcher, - Slider, - Number, - Text, - Graph, - GraphPoint, - Tab, - Select, - Option, - Min, - Max, - Step, - Gauge, - Accel, - Separator, - Time, - FileDisplay, - - Fragment = 98, - Password = 99, - UpdateOffset = 100, -}; - -enum ControlColor : uint8_t -{ - Turquoise, - Emerald, - Peterriver, - Wetasphalt, - Sunflower, - Carrot, - Alizarin, - Dark, - None = 0xFF -}; - class Control { public: - ControlType type; - uint16_t id; // just mirroring the id here for practical reasons + enum Type : uint8_t + { + // fixed Controls + Title = 0, + + // updatable Controls + Pad, + PadWithCenter, + Button, + Label, + Switcher, + Slider, + Number, + Text, + Graph, + GraphPoint, + Tab, + Select, + Option, + Min, + Max, + Step, + Gauge, + Accel, + Separator, + Time, + FileDisplay, + + Fragment = 98, + Password = 99, + UpdateOffset = 100, + }; + + enum Color : uint8_t + { + Turquoise, + Emerald, + Peterriver, + Wetasphalt, + Sunflower, + Carrot, + Alizarin, + Dark, + None = 0xFF + }; + + typedef uint16_t ControlId_t; + const char* label; std::function callback; String value; - ControlColor color; + Color color; bool visible; bool wide; bool vertical; bool enabled; - uint16_t parentControl; + ControlId_t parentControl; String panelStyle; String elementStyle; String inputType; - Control* next; static constexpr uint16_t noParent = 0xffff; - Control(ControlType type, - const char* label, + Control(ControlId_t id, + Type type, + const char* label, std::function callback, - const String& value, - ControlColor color, - bool visible, + const String& value, + Color color, + bool visible, uint16_t parentControl); Control(const Control& Control); @@ -90,8 +90,43 @@ class Control inline bool ToBeDeleted() { return _ToBeDeleted; } inline bool NeedsSync(uint32_t lastControlChangeID) {return (false == _ToBeDeleted) && (lastControlChangeID < ControlChangeID);} void SetControlChangedId(uint32_t value) {ControlChangeID = value;} + inline ControlId_t GetId() {return id;} + inline Type GetType() {return type;} + +#define UI_TITLE Control::Type::Title +#define UI_LABEL Control::Type::Label +#define UI_BUTTON Control::Type::Button +#define UI_SWITCHER Control::Type::Switcher +#define UI_PAD Control::Type::Pad +#define UI_CPAD Control::Type::Cpad +#define UI_SLIDER Control::Type::Slider +#define UI_NUMBER Control::Type::Number +#define UI_TEXT_INPUT Control::Type::Text +#define UI_GRAPH Control::Type::Graph +#define UI_ADD_GRAPH_POINT Control::Type::GraphPoint + +#define UPDATE_LABEL Control::Type::UpdateLabel +#define UPDATE_SWITCHER Control::Type::UpdateSwitcher +#define UPDATE_SLIDER Control::Type::UpdateSlider +#define UPDATE_NUMBER Control::Type::UpdateNumber +#define UPDATE_TEXT_INPUT Control::Type::UpdateText +#define CLEAR_GRAPH Control::Type::ClearGraph + +// Colors +#define COLOR_TURQUOISE Control::Color::Turquoise +#define COLOR_EMERALD Control::Color::Emerald +#define COLOR_PETERRIVER Control::Color::Peterriver +#define COLOR_WETASPHALT Control::Color::Wetasphalt +#define COLOR_SUNFLOWER Control::Color::Sunflower +#define COLOR_CARROT Control::Color::Carrot +#define COLOR_ALIZARIN Control::Color::Alizarin +#define COLOR_DARK Control::Color::Dark +#define COLOR_NONE Control::Color::None private: + Type type = Type::Title; + ControlId_t id = Control::noParent; + bool _ToBeDeleted = false; uint32_t ControlChangeID = 0; String OldValue = emptyString; @@ -102,32 +137,3 @@ class Control #define JsonMarshaledOverhead 64 }; -#define UI_TITLE ControlType::Title -#define UI_LABEL ControlType::Label -#define UI_BUTTON ControlType::Button -#define UI_SWITCHER ControlType::Switcher -#define UI_PAD ControlType::Pad -#define UI_CPAD ControlType::Cpad -#define UI_SLIDER ControlType::Slider -#define UI_NUMBER ControlType::Number -#define UI_TEXT_INPUT ControlType::Text -#define UI_GRAPH ControlType::Graph -#define UI_ADD_GRAPH_POINT ControlType::GraphPoint - -#define UPDATE_LABEL ControlType::UpdateLabel -#define UPDATE_SWITCHER ControlType::UpdateSwitcher -#define UPDATE_SLIDER ControlType::UpdateSlider -#define UPDATE_NUMBER ControlType::UpdateNumber -#define UPDATE_TEXT_INPUT ControlType::UpdateText -#define CLEAR_GRAPH ControlType::ClearGraph - -// Colors -#define COLOR_TURQUOISE ControlColor::Turquoise -#define COLOR_EMERALD ControlColor::Emerald -#define COLOR_PETERRIVER ControlColor::Peterriver -#define COLOR_WETASPHALT ControlColor::Wetasphalt -#define COLOR_SUNFLOWER ControlColor::Sunflower -#define COLOR_CARROT ControlColor::Carrot -#define COLOR_ALIZARIN ControlColor::Alizarin -#define COLOR_DARK ControlColor::Dark -#define COLOR_NONE ControlColor::None diff --git a/src/ESPUIcontrolMgr.cpp b/src/ESPUIcontrolMgr.cpp new file mode 100644 index 0000000..b3b4399 --- /dev/null +++ b/src/ESPUIcontrolMgr.cpp @@ -0,0 +1,371 @@ + +#include "ESPUIControlMgr.h" + +static Control::ControlId_t idCounter = 0; + +_ESPUIcontrolMgr::_ESPUIcontrolMgr() +{ +#ifdef ESP32 + ControlsSemaphore = xSemaphoreCreateMutex(); + xSemaphoreGive(ControlsSemaphore); +#endif // def ESP32 +} + +void _ESPUIcontrolMgr::RemoveToBeDeletedControls() +{ +#ifdef ESP32 + xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); +#endif // def ESP32 + + ControlObject_t * PreviousControl = nullptr; + ControlObject_t * CurrentControl = controls; + + while (nullptr != CurrentControl) + { + ControlObject_t * NextControl = CurrentControl->next; + if (CurrentControl->ToBeDeleted()) + { + if (CurrentControl == controls) + { + // this is the root control + controls = NextControl; + } + else + { + PreviousControl->next = NextControl; + } + delete CurrentControl; + CurrentControl = NextControl; + } + else + { + PreviousControl = CurrentControl; + CurrentControl = NextControl; + } + } +#ifdef ESP32 + xSemaphoreGive(ControlsSemaphore); +#endif // def ESP32 +} + +Control* _ESPUIcontrolMgr::getControl(Control::ControlId_t id) +{ +#ifdef ESP32 + xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); +#endif // !def ESP32 + Control* Response = getControlNoLock(id); +#ifdef ESP32 + xSemaphoreGive(ControlsSemaphore); +#endif // !def ESP32 + return Response; +} + +// WARNING: Anytime you walk the chain of controllers, the protection semaphore +// MUST be locked. This function assumes that the semaphore is locked +// at the time it is called. Make sure YOU locked it :) +Control* _ESPUIcontrolMgr::getControlNoLock(Control::ControlId_t id) +{ + return getControlObjectNoLock(id); +} // getControlNoLock + +// WARNING: Anytime you walk the chain of controllers, the protection semaphore +// MUST be locked. This function assumes that the semaphore is locked +// at the time it is called. Make sure YOU locked it :) +_ESPUIcontrolMgr::ControlObject_t * _ESPUIcontrolMgr::getControlObjectNoLock(Control::ControlId_t id) +{ + ControlObject_t * Response = nullptr; + ControlObject_t * CurrentControl = controls; + + while (nullptr != CurrentControl) + { + if (CurrentControl->GetId() == id) + { + if (!CurrentControl->ToBeDeleted()) + { + Response = CurrentControl; + } + break; + } + CurrentControl = CurrentControl->next; + } + + return Response; +} // getControlObjectNoLock + +bool _ESPUIcontrolMgr::removeControl(Control::ControlId_t id) +{ + bool Response = false; + + Control* control = getControl(id); + if (control) + { + Response = true; + control->DeleteControl(); + controlCount--; + } +#ifdef DEBUG_ESPUI + else + { + // Serial.println(String("Could not Remove Control ") + String(id)); + } +#endif // def DEBUG_ESPUI + + return Response; +} + + +/* +Prepare a chunk of elements as a single JSON string. If the allowed number of elements is greater than the total +number this will represent the entire UI. More likely, it will represent a small section of the UI to be sent. The +client will acknowledge receipt by requesting the next chunk. + */ +uint32_t _ESPUIcontrolMgr::prepareJSONChunk(uint16_t startindex, + JsonDocument & rootDoc, + bool InUpdateMode, + String FragmentRequestString, + uint32_t CurrentSyncID) +{ +#ifdef ESP32 + xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); +#endif // def ESP32 + + // Serial.println(String("prepareJSONChunk: Start. InUpdateMode: ") + String(InUpdateMode)); + // Serial.println(String("prepareJSONChunk: Start. startindex: ") + String(startindex)); + // Serial.println(String("prepareJSONChunk: Start. FragmentRequestString: '") + FragmentRequestString + "'"); + int elementcount = 0; + uint32_t MaxMarshaledJsonSize = (!InUpdateMode) ? ESPUI.jsonInitialDocumentSize: ESPUI.jsonUpdateDocumentSize; + uint32_t EstimatedUsedMarshaledJsonSize = 0; + + do // once + { + // Follow the list until control points to the startindex'th node + ControlObject_t * CurrentControlObject = controls; + uint32_t currentIndex = 0; + uint32_t DataOffset = 0; + JsonArray items = rootDoc[F("controls")]; + bool SingleControl = false; + + if(!emptyString.equals(FragmentRequestString)) + { + // Serial.println(F("prepareJSONChunk:Fragmentation:Got Header (1)")); + // Serial.println(String("prepareJSONChunk:startindex: ") + String(startindex)); + // Serial.println(String("prepareJSONChunk:currentIndex: ") + String(currentIndex)); + // Serial.println(String("prepareJSONChunk:FragmentRequestString: '") + FragmentRequestString + "'"); + + // this is actually a fragment or directed update request + // parse the string we got from the UI and try to update that specific + // control. + AllocateJsonDocument(FragmentRequest, FragmentRequestString.length() * 3); +/* + ArduinoJson::detail::sizeofObject(N); + if(0 >= FragmentRequest.capacity()) + { + Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not allocate memory for a fragmentation request. Skipping Response")); + break; + } +*/ + size_t FragmentRequestStartOffset = FragmentRequestString.indexOf("{"); + DeserializationError error = deserializeJson(FragmentRequest, FragmentRequestString.substring(FragmentRequestStartOffset)); + if(DeserializationError::Ok != error) + { + Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Could not extract json from the fragment request")); + break; + } + + if(!FragmentRequest.containsKey(F("id"))) + { + Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a control ID")); + break; + } + uint16_t ControlId = uint16_t(FragmentRequest[F("id")]); + + if(!FragmentRequest.containsKey(F("offset"))) + { + Serial.println(F("ERROR:prepareJSONChunk:Fragmentation:Request does not contain a starting offset")); + break; + } + DataOffset = uint16_t(FragmentRequest[F("offset")]); + CurrentControlObject = getControlObjectNoLock(ControlId); + if(nullptr == CurrentControlObject) + { + Serial.println(String(F("ERROR:prepareJSONChunk:Fragmentation:Requested control: ")) + String(ControlId) + F(" does not exist")); + break; + } + + // Serial.println(F("prepareJSONChunk:Fragmentation:disable the control search operation")); + currentIndex = 1; + startindex = 0; + SingleControl = true; + } + + // find a control to send + while ((startindex > currentIndex) && (nullptr != CurrentControlObject)) + { + // only count active controls + if (!CurrentControlObject->ToBeDeleted()) + { + if(InUpdateMode) + { + // In update mode we only count the controls that have been updated. + if(CurrentControlObject->NeedsSync(CurrentSyncID)) + { + ++currentIndex; + } + } + else + { + // not in update mode. Count all active controls + ++currentIndex; + } + } + CurrentControlObject = CurrentControlObject->next; + } + + // any controls left to be processed? + if(nullptr == CurrentControlObject) + { + // Serial.println("prepareJSONChunk: No controls to process"); + break; + } + + // keep track of the number of elements we have serialised into this + // message. Overflow is detected and handled later in this loop + // and needs an index to the last item added. + while (nullptr != CurrentControlObject) + { + // skip deleted controls or controls that have not been updated + if (CurrentControlObject->ToBeDeleted() && !SingleControl) + { + // Serial.println(String("prepareJSONChunk: Ignoring Deleted control: ") + String(control->id)); + CurrentControlObject = CurrentControlObject->next; + continue; + } + + if(InUpdateMode && !SingleControl) + { + if(CurrentControlObject->NeedsSync(CurrentSyncID)) + { + // dont skip this control + } + else + { + // control has not been updated. Skip it + CurrentControlObject = CurrentControlObject->next; + continue; + } + } + + // Serial.println(String(F("prepareJSONChunk: MaxMarshaledJsonSize: ")) + String(MaxMarshaledJsonSize)); + // Serial.println(String(F("prepareJSONChunk: Cur EstimatedUsedMarshaledJsonSize: ")) + String(EstimatedUsedMarshaledJsonSize)); + + JsonObject item = AllocateJsonObject(items); + elementcount++; + uint32_t RemainingSpace = (MaxMarshaledJsonSize - EstimatedUsedMarshaledJsonSize) - 100; + // Serial.println(String(F("prepareJSONChunk: RemainingSpace: ")) + String(RemainingSpace)); + uint32_t SpaceUsedByMarshaledControl = 0; + bool ControlIsFragmented = CurrentControlObject->MarshalControl(item, + InUpdateMode, + DataOffset, + RemainingSpace, + SpaceUsedByMarshaledControl); + // Serial.println(String(F("prepareJSONChunk: SpaceUsedByMarshaledControl: ")) + String(SpaceUsedByMarshaledControl)); + EstimatedUsedMarshaledJsonSize += SpaceUsedByMarshaledControl; + // Serial.println(String(F("prepareJSONChunk: New EstimatedUsedMarshaledJsonSize: ")) + String(EstimatedUsedMarshaledJsonSize)); + // Serial.println(String(F("prepareJSONChunk: ControlIsFragmented: ")) + String(ControlIsFragmented)); + + // did the control get added to the doc? + if (0 == SpaceUsedByMarshaledControl || + (ESPUI.jsonChunkNumberMax > 0 && (elementcount % ESPUI.jsonChunkNumberMax) == 0)) + { + // Serial.println( String("prepareJSONChunk: too much data in the message. Remove the last entry")); + if (1 == elementcount) + { + // Serial.println(String(F("prepareJSONChunk: Control ")) + String(control->id) + F(" is too large to be sent to the browser.")); + // Serial.println(String(F("ERROR: prepareJSONChunk: value: ")) + control->value); + rootDoc.clear(); + item = AllocateJsonObject(items); + CurrentControlObject->MarshalErrorMessage(item); + elementcount = 0; + } + else + { + // Serial.println(String("prepareJSONChunk: Defering control: ") + String(control->id)); + // Serial.println(String("prepareJSONChunk: elementcount: ") + String(elementcount)); + + items.remove(elementcount); + --elementcount; + } + // exit the loop + CurrentControlObject = nullptr; + } + else if ((SingleControl) || + (ControlIsFragmented) || + (MaxMarshaledJsonSize < (EstimatedUsedMarshaledJsonSize + 100))) + { + // Serial.println("prepareJSONChunk: Doc is Full, Fragmented Control or Single Control. exit loop"); + CurrentControlObject = nullptr; + } + else + { + // Serial.println("prepareJSONChunk: Next Control"); + CurrentControlObject = CurrentControlObject->next; + } + } // end while (control != nullptr) + + } while (false); + +#ifdef ESP32 + xSemaphoreGive(ControlsSemaphore); +#endif // def ESP32 + + // Serial.println(String("prepareJSONChunk: END: elementcount: ") + String(elementcount)); + return elementcount; +} + + +Control::ControlId_t _ESPUIcontrolMgr::addControl(Control::Type type, + const char* label, + const String& value, + Control::Color color, + Control::ControlId_t parentControl, + bool visible, + std::function callback) +{ + // Create a Wrapper and a control + ControlObject_t * NewControlObject = new ControlObject_t(++idCounter, type, label, callback, value, color, visible, parentControl); + NewControlObject->next = nullptr; + +#ifdef ESP32 + xSemaphoreTake(ControlsSemaphore, portMAX_DELAY); +#endif // def ESP32 + + if (controls == nullptr) + { + controls = NewControlObject; + } + else + { + ControlObject_t * iterator = controls; + while (iterator->next != nullptr) + { + iterator = iterator->next; + } + iterator->next = NewControlObject; + } + + controlCount++; + +#ifdef ESP32 + xSemaphoreGive(ControlsSemaphore); +#endif // def ESP32 + + return NewControlObject->GetId(); +} + +_ESPUIcontrolMgr::ControlObject_t::ControlObject_t(Control::ControlId_t id, Control::Type type, const char* label, std::function callback, + const String& value, Control::Color color, bool visible, ControlId_t parentControl) + : Control(id, type, label, callback, value, color, visible, parentControl) +{} + +// Instantiate the singleton +_ESPUIcontrolMgr ESPUIcontrolMgr; diff --git a/src/ESPUIcontrolMgr.h b/src/ESPUIcontrolMgr.h new file mode 100644 index 0000000..62cfa45 --- /dev/null +++ b/src/ESPUIcontrolMgr.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +// implemented as a singleton +class _ESPUIcontrolMgr +{ +public: + _ESPUIcontrolMgr(); + ~_ESPUIcontrolMgr() {} + _ESPUIcontrolMgr (_ESPUIcontrolMgr const&) = delete; + void operator=(_ESPUIcontrolMgr const&) = delete; + + Control::ControlId_t addControl(Control::Type type, + const char* label, + const String& value, + Control::Color color, + Control::ControlId_t parentControl, + bool visible, + std::function callback); + bool removeControl(Control::ControlId_t id); + void RemoveToBeDeletedControls(); + + Control* getControl(Control::ControlId_t id); + Control* getControlNoLock(Control::ControlId_t id); + Control::ControlId_t GetControlCount() {return controlCount;} + + uint32_t prepareJSONChunk(uint16_t startindex, + JsonDocument & rootDoc, + bool InUpdateMode, + String FragmentRequestString, + uint32_t CurrentSyncID); +private: + class ControlObject_t : public Control + { +public: + ControlObject_t(Control::ControlId_t id, + Type type, + const char* label, + std::function callback, + const String& value, + Color color, + bool visible, + uint16_t parentControl); + + ControlObject_t * next = nullptr; + }; // ControlObject_t + + ControlObject_t * controls = nullptr; + Control::ControlId_t controlCount = 0; + Control::ControlId_t idCounter = 0; + ControlObject_t * getControlObjectNoLock(Control::ControlId_t id); + +#ifdef ESP32 + SemaphoreHandle_t ControlsSemaphore = NULL; +#endif // def ESP32 + +}; + +extern _ESPUIcontrolMgr ESPUIcontrolMgr; \ No newline at end of file