dialogs can have a button respond to Escape
I'm planning to make it so the Enter key will never cancel a yes/no dialog. To do this, I'm adding an Escape button to dialogs. So cancel/accept keyboard shortcuts will be predictable and intuitive. Dialogs that require extra confirmation will have a 'really confirm' LED.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||||
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
|
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
|
||||||
<dialog defbtn='okay'>
|
<dialog defbtn='okay' escbtn='okay'>
|
||||||
<pict type='dlog' num='16' top='8' left='8'/>
|
<pict type='dlog' num='16' top='8' left='8'/>
|
||||||
<text top='8' left='50' width='400' height='16'>DEBUG MODE HELP</text>
|
<text top='8' left='50' width='400' height='16'>DEBUG MODE HELP</text>
|
||||||
<text top='22' left='50' width='400' height='64'>
|
<text top='22' left='50' width='400' height='64'>
|
||||||
|
@@ -398,6 +398,7 @@
|
|||||||
<xs:attribute name="debug" default="false" type="bool"/>
|
<xs:attribute name="debug" default="false" type="bool"/>
|
||||||
<xs:attribute name="fore" default="black"/>
|
<xs:attribute name="fore" default="black"/>
|
||||||
<xs:attribute name="defbtn" type="xs:token"/>
|
<xs:attribute name="defbtn" type="xs:token"/>
|
||||||
|
<xs:attribute name="escbtn" type="xs:token"/>
|
||||||
</xs:complexType>
|
</xs:complexType>
|
||||||
<xs:unique name="FieldTabOrder">
|
<xs:unique name="FieldTabOrder">
|
||||||
<xs:selector xpath="*" />
|
<xs:selector xpath="*" />
|
||||||
@@ -411,6 +412,10 @@
|
|||||||
<xs:selector xpath="."/>
|
<xs:selector xpath="."/>
|
||||||
<xs:field xpath="@defbtn"/>
|
<xs:field xpath="@defbtn"/>
|
||||||
</xs:keyref>
|
</xs:keyref>
|
||||||
|
<xs:keyref name="escapeButton" refer="uniqueID">
|
||||||
|
<xs:selector xpath="."/>
|
||||||
|
<xs:field xpath="@escbtn"/>
|
||||||
|
</xs:keyref>
|
||||||
<xs:keyref name="sliderLink" refer="uniqueID">
|
<xs:keyref name="sliderLink" refer="uniqueID">
|
||||||
<xs:selector xpath="slider"/>
|
<xs:selector xpath="slider"/>
|
||||||
<xs:field xpath="@link"/>
|
<xs:field xpath="@link"/>
|
||||||
|
@@ -158,7 +158,7 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
|||||||
|
|
||||||
Iterator<Attribute> attr;
|
Iterator<Attribute> attr;
|
||||||
Iterator<Element> node;
|
Iterator<Element> node;
|
||||||
string type, name, val, defbtn;
|
string type, name, val, defbtn, escbtn;
|
||||||
|
|
||||||
xml.FirstChildElement()->GetValue(&type);
|
xml.FirstChildElement()->GetValue(&type);
|
||||||
if(type != "dialog") throw xBadNode(type,xml.FirstChildElement()->Row(),xml.FirstChildElement()->Column(),fname);
|
if(type != "dialog") throw xBadNode(type,xml.FirstChildElement()->Row(),xml.FirstChildElement()->Column(),fname);
|
||||||
@@ -181,8 +181,10 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
|||||||
throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname);
|
throw xBadVal("text",name,val,attr->Row(),attr->Column(),fname);
|
||||||
}
|
}
|
||||||
defTextClr = clr;
|
defTextClr = clr;
|
||||||
} else if(name == "defbtn") {
|
}else if(name == "defbtn"){
|
||||||
defbtn = val;
|
defbtn = val;
|
||||||
|
}else if(name == "escbtn"){
|
||||||
|
escbtn = val;
|
||||||
}else if(name != "debug")
|
}else if(name != "debug")
|
||||||
throw xBadAttr(type,name,attr->Row(),attr->Column(),fname);
|
throw xBadAttr(type,name,attr->Row(),attr->Column(),fname);
|
||||||
}
|
}
|
||||||
@@ -337,6 +339,8 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
|||||||
|
|
||||||
// Set the default button.
|
// Set the default button.
|
||||||
setDefaultButton(defbtn);
|
setDefaultButton(defbtn);
|
||||||
|
// Set the escape button.
|
||||||
|
setEscapeButton(escbtn);
|
||||||
|
|
||||||
// Sort by tab order
|
// Sort by tab order
|
||||||
// First, fill any gaps that might have been left, using ones that had no specific tab order
|
// First, fill any gaps that might have been left, using ones that had no specific tab order
|
||||||
@@ -912,11 +916,12 @@ bool cDialog::addLabelFor(std::string key, std::string label, eLabelPos where, s
|
|||||||
void cDialog::process_keystroke(cKey keyHit){
|
void cDialog::process_keystroke(cKey keyHit){
|
||||||
ctrlIter iter = controls.begin();
|
ctrlIter iter = controls.begin();
|
||||||
bool enterKeyHit = keyHit.spec && keyHit.k == key_enter;
|
bool enterKeyHit = keyHit.spec && keyHit.k == key_enter;
|
||||||
|
bool escapeKeyHit = keyHit.spec && keyHit.k == key_esc;
|
||||||
while(iter != controls.end()){
|
while(iter != controls.end()){
|
||||||
cControl* ctrl = iter->second;
|
cControl* ctrl = iter->second;
|
||||||
if(ctrl->isVisible()){
|
if(ctrl->isVisible()){
|
||||||
if(ctrl->isClickable() &&
|
if(ctrl->isClickable() &&
|
||||||
(ctrl->getAttachedKey() == keyHit || (ctrl->isDefault() && enterKeyHit))){
|
(ctrl->getAttachedKey() == keyHit || (ctrl->isDefault() && enterKeyHit) || (ctrl->isEscape() && escapeKeyHit))){
|
||||||
|
|
||||||
ctrl->handleKeyTriggered(*this);
|
ctrl->handleKeyTriggered(*this);
|
||||||
return;
|
return;
|
||||||
@@ -940,11 +945,6 @@ void cDialog::process_keystroke(cKey keyHit){
|
|||||||
}
|
}
|
||||||
iter++;
|
iter++;
|
||||||
}
|
}
|
||||||
// If you get an escape and it isn't processed, make it an enter.
|
|
||||||
if(keyHit.spec && keyHit.k == key_esc){
|
|
||||||
keyHit.k = key_enter;
|
|
||||||
process_keystroke(keyHit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDialog::process_click(location where, eKeyMod mods, cFramerateLimiter& fps_limiter){
|
void cDialog::process_click(location where, eKeyMod mods, cFramerateLimiter& fps_limiter){
|
||||||
@@ -1157,21 +1157,38 @@ bool cDialog::hasControl(std::string id) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cDialog::setDefaultButton(std::string defbtn) {
|
void cDialog::setSpecialButton(std::string& name_ref, std::string name, bool escape) {
|
||||||
if(!defbtn.empty() && !hasControl(defbtn)){
|
if(!name.empty() && !hasControl(name)){
|
||||||
// this is likely because the dialogxml is malformed. maybe the linter already checks this,
|
// this is likely because the dialogxml is malformed. maybe the linter already checks this,
|
||||||
// but the engine might as well also.
|
// but the engine might as well also.
|
||||||
throw std::string { "Requested default button does not exist: " } + defbtn;
|
throw std::string { "Requested button does not exist: " } + name;
|
||||||
}
|
}
|
||||||
if(!defaultButton.empty()){
|
if(!name_ref.empty()){
|
||||||
getControl(defaultButton).setDefault(false);
|
if(escape){
|
||||||
|
getControl(name_ref).setEscape(false);
|
||||||
|
}else{
|
||||||
|
getControl(name_ref).setDefault(false);
|
||||||
|
}
|
||||||
|
name_ref = "";
|
||||||
}
|
}
|
||||||
if(!defbtn.empty()){
|
if(!name.empty()){
|
||||||
defaultButton = defbtn;
|
name_ref = name;
|
||||||
getControl(defaultButton).setDefault(true);
|
if(escape){
|
||||||
|
getControl(name).setEscape(true);
|
||||||
|
}else{
|
||||||
|
getControl(name).setDefault(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cDialog::setDefaultButton(std::string defbtn) {
|
||||||
|
setSpecialButton(defaultButton, defbtn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cDialog::setEscapeButton(std::string escbtn) {
|
||||||
|
setSpecialButton(escapeButton, escbtn, true);
|
||||||
|
}
|
||||||
|
|
||||||
const char*const xBadVal::CONTENT = "$content$";
|
const char*const xBadVal::CONTENT = "$content$";
|
||||||
|
|
||||||
cDialogIterator::cDialogIterator() : parent(nullptr) {}
|
cDialogIterator::cDialogIterator() : parent(nullptr) {}
|
||||||
|
@@ -266,7 +266,9 @@ public:
|
|||||||
inline void setAnimPictFPS(int fps) { if(fps == -1) fps = 2; anim_pict_fps = fps; }
|
inline void setAnimPictFPS(int fps) { if(fps == -1) fps = 2; anim_pict_fps = fps; }
|
||||||
inline void setDoAnimations(bool value) { doAnimations = value; }
|
inline void setDoAnimations(bool value) { doAnimations = value; }
|
||||||
void setDefaultButton(std::string defbtn);
|
void setDefaultButton(std::string defbtn);
|
||||||
|
void setEscapeButton(std::string escbtn);
|
||||||
private:
|
private:
|
||||||
|
void setSpecialButton(std::string& name_ref, std::string name, bool escape);
|
||||||
void draw();
|
void draw();
|
||||||
void handle_events();
|
void handle_events();
|
||||||
void handle_one_event(const sf::Event&, cFramerateLimiter& fps_limiter);
|
void handle_one_event(const sf::Event&, cFramerateLimiter& fps_limiter);
|
||||||
@@ -277,6 +279,7 @@ private:
|
|||||||
boost::any result;
|
boost::any result;
|
||||||
std::string fname;
|
std::string fname;
|
||||||
std::string defaultButton;
|
std::string defaultButton;
|
||||||
|
std::string escapeButton;
|
||||||
sf::Clock animTimer, paintTimer;
|
sf::Clock animTimer, paintTimer;
|
||||||
friend class cControl;
|
friend class cControl;
|
||||||
friend class cContainer;
|
friend class cContainer;
|
||||||
|
@@ -172,6 +172,8 @@ public:
|
|||||||
std::string getAttachedKeyDescription() const;
|
std::string getAttachedKeyDescription() const;
|
||||||
inline void setDefault(bool value) { isDefaultControl = value; }
|
inline void setDefault(bool value) { isDefaultControl = value; }
|
||||||
inline bool isDefault() { return isDefaultControl; }
|
inline bool isDefault() { return isDefaultControl; }
|
||||||
|
inline void setEscape(bool value) { isEscapeControl = value; }
|
||||||
|
inline bool isEscape() { return isEscapeControl; }
|
||||||
/// Attach an event handler to this control.
|
/// Attach an event handler to this control.
|
||||||
/// @tparam t The type of event to attach.
|
/// @tparam t The type of event to attach.
|
||||||
/// @param handler The event handler function or functor. Its signature depends on the event type.
|
/// @param handler The event handler function or functor. Its signature depends on the event type.
|
||||||
@@ -477,6 +479,8 @@ protected:
|
|||||||
cKey key;
|
cKey key;
|
||||||
/// Whether the control is the default control of its dialog.
|
/// Whether the control is the default control of its dialog.
|
||||||
bool isDefaultControl = false;
|
bool isDefaultControl = false;
|
||||||
|
/// Whether the control is the cancel control of its dialog.
|
||||||
|
bool isEscapeControl = false;
|
||||||
|
|
||||||
/// Draw a frame around the control.
|
/// Draw a frame around the control.
|
||||||
/// @param amt How much to offset the frame from the control's bounding rect.
|
/// @param amt How much to offset the frame from the control's bounding rect.
|
||||||
|
@@ -75,8 +75,10 @@ such, this attribute should be omitted for most dialogs.
|
|||||||
to `true`, the XSL stylesheet will draw the bounding rects of LEDs and
|
to `true`, the XSL stylesheet will draw the bounding rects of LEDs and
|
||||||
other debug information.
|
other debug information.
|
||||||
* `fore` - The default text colour. Generally this shouldn't be needed.
|
* `fore` - The default text colour. Generally this shouldn't be needed.
|
||||||
* `defbtn` - The ID (`name` attribute) of the default button. This is an
|
* `defbtn` - The ID (`name` attribute) of button triggered by Enter/Return.
|
||||||
IDREF, so it must exist in the dialog.
|
This is an IDREF, so it must exist in the dialog.
|
||||||
|
* `escbtn` - The ID (`name` attribute) of button triggered by Escape.
|
||||||
|
This is an IDREF, so it must exist in the dialog.
|
||||||
|
|
||||||
The `<text>` tag
|
The `<text>` tag
|
||||||
----------------
|
----------------
|
||||||
|
Reference in New Issue
Block a user