Merge pull request #154 from iangray001/enabledisable

Support for Enabling and Disabling controls
This commit is contained in:
Ian Gray 2022-02-12 17:43:06 +00:00 committed by GitHub
commit e3e5c34953
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 223 additions and 48 deletions

View File

@ -37,6 +37,7 @@ The Library runs on any kind of **ESP8266** and **ESP32** (NodeMCU, AI Thinker,
- [Advanced Features](#advanced-features)
* [Dynamic Visibility](#dynamic-visibility)
* [Inline Styles](#inline-styles)
* [Disabling Controls](#disabling-controls)
* [Grouped controls](#grouped-controls)
* [Wide controls](#wide-controls)
* [Graph (Experimental)](#graph--experimental-)
@ -470,6 +471,36 @@ The [completeExample](examples/completeExample/completeExample.cpp) example incl
![More Inline Styles](docs/ui_inlinestyles2.png)
### Disabling Controls
It is possible to dynamically enable and disable controls to, for example, provide feedback to the user that a particular feature is
temporarily unavailable. To do this use the following function call:
```
ESPUI.setEnabled(controlId, enabled);
```
Setting `enabled` to false will make the control noninteractive and it will visually change to illustrate this to the user. The control
will stop firing any events. Note that whilst the widget will change appearance, the panel of the control will remain whatever colour
it was set to. If you wish to also change the colour of the panel then you should use inline styles to show the noninteractive state. For example:
```
ESPUI.setEnabled(mainButton, false);
const String disabledstyle = "background-color: #bbb; border-bottom: #999 3px solid;";
ESPUI.setPanelStyle(mainButton, disabledstyle);
```
This CSS style sets the panel background and its border to grey. To put the control back to enabled use the following:
```
ESPUI.setEnabled(mainButton, true);
ESPUI.setPanelStyle(mainButton, ";");
```
Note that we have to set the inline style to `";"` (i.e. an empty CSS rule) because if we just try to set it to `""` this will be
interpreted as "do not change the style".
Controls can also be set to disabled before the UI is started.
### Grouped controls

View File

@ -422,7 +422,7 @@ button {
background-color: #999999;
}
button:active {
button:enabled:active {
background-color: #666666;
transform: translateX(4px) translateY(4px);
}
@ -587,6 +587,13 @@ hr {
background-color: #eee;
}
.control:not(.disabled) a.confirm:active {
background-color:#777
}
.control:not(.disabled) li:active {
background-color:#777
}
/* Switch
*/
@ -1148,3 +1155,46 @@ text {
.vert-slider span {
transform: rotate(90deg);
}
/* Styles to implement disabled controls */
button:disabled {
color: #333;
background-color: #999;
}
select:disabled {
color: #333;
background-color: #999;
}
input:disabled {
color: #333;
background-color: #999;
}
.range-slider__range:disabled {
background-color: #999;
}
.range-slider__range:disabled::-webkit-slider-thumb {
background-color: #aaa;
}
.range-slider__range:disabled::-moz-range-thumb {
background-color: #aaa;
}
.switch.disabled .in::before {
background:#bbb;
border: 1px solid #ddd;
}
.switch.disabled .in::after {
background:#bbb;
}
.switch.checked.disabled {
background: #b1d092;
}

File diff suppressed because one or more lines are too long

137
data/js/controls.js vendored
View File

@ -433,24 +433,27 @@ function start() {
case UI_MIN:
if (data.parentControl) {
var parent = $("#id" + data.parentControl + " input");
if (parent.size()) {
parent.attr("min", data.value);
//Is it applied to a slider?
if($('#sl' + data.parentControl).length) {
$('#sl' + data.parentControl).attr("min", data.value);
} else if($('#num' + data.parentControl).length) {
//Or a number
$('#num' + data.parentControl).attr("min", data.value);
}
}
break;
case UI_MAX:
if (data.parentControl) {
var parent = $("#id" + data.parentControl + " input");
if (parent.size()) {
if(!parent.attr("type")) {
//type is not set so therefore it is a text input
parent.attr("maxlength", data.value);
} else {
//type might be range (slider) or number
parent.attr("max", data.value);
}
//Is it applied to a slider?
if($('#sl' + data.parentControl).length) {
$('#sl' + data.parentControl).attr("max", data.value);
} else if($('#text' + data.parentControl).length) {
//Is it a text element
$('#text' + data.parentControl).attr("maxlength", data.value);
} else if($('#num' + data.parentControl).length) {
//Or a number
$('#num' + data.parentControl).attr("max", data.value);
}
}
break;
@ -568,7 +571,13 @@ function start() {
break;
}
if (data.type >= UI_TITEL && data.type < UPDATE_OFFSET) {
//A UI element was just added to the DOM
processEnabled(data);
}
if (data.type >= UPDATE_OFFSET && data.type < UI_INITIAL_GUI) {
//An "update" message was just recieved and processed
var element = $("#id" + data.id);
if(data.hasOwnProperty('panelStyle')) {
@ -593,6 +602,8 @@ function start() {
);
element.addClass(colorClass(data.color));
}
processEnabled(data);
}
$(".range-slider__range").each(function(){
@ -639,27 +650,29 @@ function buttonclick(number, isdown) {
}
function padclick(type, number, isdown) {
switch (type) {
case CENTER:
if (isdown) websock.send("pcdown:" + number);
else websock.send("pcup:" + number);
break;
case UP:
if (isdown) websock.send("pfdown:" + number);
else websock.send("pfup:" + number);
break;
case DOWN:
if (isdown) websock.send("pbdown:" + number);
else websock.send("pbup:" + number);
break;
case LEFT:
if (isdown) websock.send("pldown:" + number);
else websock.send("plup:" + number);
break;
case RIGHT:
if (isdown) websock.send("prdown:" + number);
else websock.send("prup:" + number);
break;
if(!$("#id" + number + " nav").hasClass("disabled")) {
switch (type) {
case CENTER:
if (isdown) websock.send("pcdown:" + number);
else websock.send("pcup:" + number);
break;
case UP:
if (isdown) websock.send("pfdown:" + number);
else websock.send("pfup:" + number);
break;
case DOWN:
if (isdown) websock.send("pbdown:" + number);
else websock.send("pbup:" + number);
break;
case LEFT:
if (isdown) websock.send("pldown:" + number);
else websock.send("plup:" + number);
break;
case RIGHT:
if (isdown) websock.send("prdown:" + number);
else websock.send("prup:" + number);
break;
}
}
}
@ -816,4 +829,60 @@ var elementHTML = function(data) {
default:
return "";
}
}
}
var processEnabled = function(data) {
//Handle the enabling and disabling of controls
//Most controls can be disabled through the use of $("#<item>").prop("disabled", true) and CSS will style it accordingly
//The switcher and pads also require the addition of the "disabled" class
switch(data.type) {
case UI_SWITCHER:
case UPDATE_SWITCHER:
if(data.enabled) {
$("#sl" + data.id).removeClass('disabled');
$("#s" + data.id).prop("disabled", false);
} else {
$("#sl" + data.id).addClass('disabled');
$("#s" + data.id).prop("disabled", true);
}
break;
case UI_SLIDER:
case UPDATE_SLIDER:
$("#sl" + data.id).prop("disabled", !data.enabled);
break;
case UI_NUMBER:
case UPDATE_NUMBER:
$("#num" + data.id).prop("disabled", !data.enabled);
break;
case UI_TEXT_INPUT:
case UPDATE_TEXT_INPUT:
$("#text" + data.id).prop("disabled", !data.enabled);
break;
case UI_SELECT:
case UPDATE_SELECT:
$("#select" + data.id).prop("disabled", !data.enabled);
break;
case UI_BUTTON:
case UPDATE_BUTTON:
$("#btn" + data.id).prop("disabled", !data.enabled);
break;
case UI_PAD:
case UI_CPAD:
case UPDATE_PAD:
case UPDATE_CPAD:
if(data.enabled) {
$("#id" + data.id + " nav").removeClass('disabled');
} else {
$("#id" + data.id + " nav").addClass('disabled');
}
break;
}
}

View File

@ -25,8 +25,8 @@ data.selected+
">"+
data.label+
"</option>");}
break;case UI_MIN:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("min",data.value);}}
break;case UI_MAX:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){if(!parent.attr("type")){parent.attr("maxlength",data.value);}else{parent.attr("max",data.value);}}}
break;case UI_MIN:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("min",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("min",data.value);}}
break;case UI_MAX:if(data.parentControl){if($('#sl'+data.parentControl).length){$('#sl'+data.parentControl).attr("max",data.value);}else if($('#text'+data.parentControl).length){$('#text'+data.parentControl).attr("maxlength",data.value);}else if($('#num'+data.parentControl).length){$('#num'+data.parentControl).attr("max",data.value);}}
break;case UI_STEP:if(data.parentControl){var parent=$("#id"+data.parentControl+" input");if(parent.size()){parent.attr("step",data.value);}}
break;case UI_GRAPH:if(data.visible){addToHTML(data);graphData[data.id]=restoreGraphData(data.id);renderGraphSvg(graphData[data.id],"graph"+data.id);}
break;case ADD_GRAPH_POINT:var ts=Math.round(new Date().getTime()/1000);graphData[data.id].push({x:ts,y:data.value});saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case CLEAR_GRAPH:graphData[data.id]=[];saveGraphData();renderGraphSvg(graphData[data.id],"graph"+data.id);break;case UI_ACCEL:if(hasAccel)break;hasAccel=true;if(data.visible){addToHTML(data);requestOrientationPermission();}
@ -40,11 +40,13 @@ break;case UPDATE_SELECT:$("#select"+data.id).val(data.value);if(data.hasOwnProp
break;case UPDATE_BUTTON:$("#btn"+data.id).val(data.value);$("#btn"+data.id).text(data.value);if(data.hasOwnProperty('elementStyle')){$("#btn"+data.id).attr("style",data.elementStyle);}
break;case UPDATE_PAD:case UPDATE_CPAD:break;case UPDATE_GAUGE:$("#gauge"+data.id).val(data.value);if(data.hasOwnProperty('elementStyle')){$("#gauge"+data.id).attr("style",data.elementStyle);}
break;case UPDATE_ACCEL:break;case UPDATE_TIME:var rv=new Date().toISOString();websock.send("time:"+rv+":"+data.id);break;default:console.error("Unknown type or event");break;}
if(data.type>=UI_TITEL&&data.type<UPDATE_OFFSET){processEnabled(data);}
if(data.type>=UPDATE_OFFSET&&data.type<UI_INITIAL_GUI){var element=$("#id"+data.id);if(data.hasOwnProperty('panelStyle')){$("#id"+data.id).attr("style",data.panelStyle);}
if(data.hasOwnProperty('visible')){if(data['visible'])
$("#id"+data.id).show();else
$("#id"+data.id).hide();}
if(data.type==UPDATE_SLIDER){element.removeClass("slider-turquoise slider-emerald slider-peterriver slider-wetasphalt slider-sunflower slider-carrot slider-alizarin");element.addClass("slider-"+colorClass(data.color));}else{element.removeClass("turquoise emerald peterriver wetasphalt sunflower carrot alizarin");element.addClass(colorClass(data.color));}}
if(data.type==UPDATE_SLIDER){element.removeClass("slider-turquoise slider-emerald slider-peterriver slider-wetasphalt slider-sunflower slider-carrot slider-alizarin");element.addClass("slider-"+colorClass(data.color));}else{element.removeClass("turquoise emerald peterriver wetasphalt sunflower carrot alizarin");element.addClass(colorClass(data.color));}
processEnabled(data);}
$(".range-slider__range").each(function(){$(this)[0].value=$(this).attr("value");$(this).next().html($(this).attr("value"));});};websock.onmessage=handleEvent;}
function sliderchange(number){var val=$("#sl"+number).val();websock.send("slvalue:"+val+":"+number);$(".range-slider__range").each(function(){$(this).attr("value",$(this)[0].value);});}
function numberchange(number){var val=$("#num"+number).val();websock.send("nvalue:"+val+":"+number);}
@ -52,7 +54,7 @@ function textchange(number){var val=$("#text"+number).val();websock.send("tvalue
function tabclick(number){var val=$("#tab"+number).val();websock.send("tabvalue:"+val+":"+number);}
function selectchange(number){var val=$("#select"+number).val();websock.send("svalue:"+val+":"+number);}
function buttonclick(number,isdown){if(isdown)websock.send("bdown:"+number);else websock.send("bup:"+number);}
function padclick(type,number,isdown){switch(type){case CENTER:if(isdown)websock.send("pcdown:"+number);else websock.send("pcup:"+number);break;case UP:if(isdown)websock.send("pfdown:"+number);else websock.send("pfup:"+number);break;case DOWN:if(isdown)websock.send("pbdown:"+number);else websock.send("pbup:"+number);break;case LEFT:if(isdown)websock.send("pldown:"+number);else websock.send("plup:"+number);break;case RIGHT:if(isdown)websock.send("prdown:"+number);else websock.send("prup:"+number);break;}}
function padclick(type,number,isdown){if(!$("#id"+number+" nav").hasClass("disabled")){switch(type){case CENTER:if(isdown)websock.send("pcdown:"+number);else websock.send("pcup:"+number);break;case UP:if(isdown)websock.send("pfdown:"+number);else websock.send("pfup:"+number);break;case DOWN:if(isdown)websock.send("pbdown:"+number);else websock.send("pbup:"+number);break;case LEFT:if(isdown)websock.send("pldown:"+number);else websock.send("plup:"+number);break;case RIGHT:if(isdown)websock.send("prdown:"+number);else websock.send("prup:"+number);break;}}}
function switcher(number,state){if(state==null){if(!$("#sl"+number).hasClass("checked")){websock.send("sactive:"+number);$("#sl"+number).addClass("checked");}else{websock.send("sinactive:"+number);$("#sl"+number).removeClass("checked");}}else if(state==1){$("#sl"+number).addClass("checked");$("#sl"+number).prop("checked",true);}else if(state==0){$("#sl"+number).removeClass("checked");$("#sl"+number).prop("checked",false);}}
var rangeSlider=function(isDiscrete){var range=$(".range-slider__range");var slidercb=function(){sliderchange($(this).attr("id").replace(/^\D+/g,""));};range.on({input:function(){$(this).next().html(this.value)}});range.each(function(){$(this).next().html(this.value);if($(this).attr("callbackSet")!="true"){if(!isDiscrete){$(this).on({input:slidercb});}else{$(this).on({change:slidercb});}
$(this).attr("callbackSet","true");}});};var addToHTML=function(data){panelStyle=data.hasOwnProperty('panelStyle')?" style='"+data.panelStyle+"' ":"";panelwide=data.hasOwnProperty('wide')?"wide":"";if(!data.hasOwnProperty('parentControl')||$("#tab"+data.parentControl).length>0){var parent=data.hasOwnProperty('parentControl')?$("#tab"+data.parentControl):$("#row");var html="";switch(data.type){case UI_LABEL:case UI_BUTTON:case UI_SWITCHER:case UI_CPAD:case UI_PAD:case UI_SLIDER:case UI_NUMBER:case UI_TEXT_INPUT:case UI_SELECT:case UI_GRAPH:case UI_GAUGE:case UI_ACCEL:html="<div id='id"+data.id+"' "+panelStyle+" class='two columns "+panelwide+" card tcenter "+
@ -94,4 +96,7 @@ data.value+"</span></div>";case UI_NUMBER:return"<input style='color:black;' "+e
"' value='"+data.value+"' onchange='textchange("+id+")' />";case UI_SELECT:return"<select style='color:black;' "+elementStyle+" id='select"+id+
"' onchange='selectchange("+id+")' />";case UI_GRAPH:return"<figure id='graph"+id+"'><figcaption>"+data.label+"</figcaption></figure>";case UI_GAUGE:return"WILL BE A GAUGE <input style='color:black;' id='gauge"+id+
"' type='number' value='"+data.value+"' onchange='numberchange("+id+")' />";case UI_ACCEL:return"ACCEL // Not implemented fully!<div class='accelerometer' id='accel"+id+
"' ><div class='ball"+id+"'></div><pre class='accelerometeroutput"+id+"'></pre>";default:return"";}}
"' ><div class='ball"+id+"'></div><pre class='accelerometeroutput"+id+"'></pre>";default:return"";}}
var processEnabled=function(data){switch(data.type){case UI_SWITCHER:case UPDATE_SWITCHER:if(data.enabled){$("#sl"+data.id).removeClass('disabled');$("#s"+data.id).prop("disabled",false);}else{$("#sl"+data.id).addClass('disabled');$("#s"+data.id).prop("disabled",true);}
break;case UI_SLIDER:case UPDATE_SLIDER:$("#sl"+data.id).prop("disabled",!data.enabled);break;case UI_NUMBER:case UPDATE_NUMBER:$("#num"+data.id).prop("disabled",!data.enabled);break;case UI_TEXT_INPUT:case UPDATE_TEXT_INPUT:$("#text"+data.id).prop("disabled",!data.enabled);break;case UI_SELECT:case UPDATE_SELECT:$("#select"+data.id).prop("disabled",!data.enabled);break;case UI_BUTTON:case UPDATE_BUTTON:$("#btn"+data.id).prop("disabled",!data.enabled);break;case UI_PAD:case UI_CPAD:case UPDATE_PAD:case UPDATE_CPAD:if(data.enabled){$("#id"+data.id+" nav").removeClass('disabled');}else{$("#id"+data.id+" nav").addClass('disabled');}
break;}}

View File

@ -783,6 +783,7 @@ void ESPUIClass::updateControl(Control* control, int clientId)
root["id"] = control->id;
root["visible"] = control->visible;
root["color"] = (int)control->color;
root["enabled"] = control->enabled;
if (control->panelStyle.length())
root["panelStyle"] = control->panelStyle;
if (control->elementStyle.length())
@ -855,6 +856,16 @@ void ESPUIClass::setPanelWide(uint16_t id, bool wide) {
}
}
void ESPUIClass::setEnabled(uint16_t id, bool enabled, int clientId) {
Control* control = getControl(id);
if (control)
{
control->enabled = enabled;
updateControl(control, clientId);
}
}
void ESPUIClass::setVertical(uint16_t id, bool vert) {
Control* control = getControl(id);
if (control)
@ -1108,6 +1119,7 @@ void ESPUIClass::prepareJSONChunk(AsyncWebSocketClient* client, uint16_t startin
item["value"] = String(control->value);
item["color"] = (int)control->color;
item["visible"] = (int)control->visible;
item["enabled"] = control->enabled;
if (control->panelStyle.length())
item["panelStyle"] = String(control->panelStyle);
if (control->elementStyle.length())

View File

@ -142,6 +142,7 @@ public:
bool visible;
bool wide;
bool vertical;
bool enabled;
uint16_t parentControl;
String panelStyle;
String elementStyle;
@ -301,6 +302,8 @@ public:
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 updateVisibility(uint16_t id, bool visibility, int clientId = -1);
// Variables

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long