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-stylesheet href="dialog.xsl" type="text/xsl"?>
|
||||
<dialog defbtn='okay'>
|
||||
<dialog defbtn='okay' escbtn='okay'>
|
||||
<pict type='dlog' num='16' top='8' left='8'/>
|
||||
<text top='8' left='50' width='400' height='16'>DEBUG MODE HELP</text>
|
||||
<text top='22' left='50' width='400' height='64'>
|
||||
|
@@ -398,6 +398,7 @@
|
||||
<xs:attribute name="debug" default="false" type="bool"/>
|
||||
<xs:attribute name="fore" default="black"/>
|
||||
<xs:attribute name="defbtn" type="xs:token"/>
|
||||
<xs:attribute name="escbtn" type="xs:token"/>
|
||||
</xs:complexType>
|
||||
<xs:unique name="FieldTabOrder">
|
||||
<xs:selector xpath="*" />
|
||||
@@ -411,6 +412,10 @@
|
||||
<xs:selector xpath="."/>
|
||||
<xs:field xpath="@defbtn"/>
|
||||
</xs:keyref>
|
||||
<xs:keyref name="escapeButton" refer="uniqueID">
|
||||
<xs:selector xpath="."/>
|
||||
<xs:field xpath="@escbtn"/>
|
||||
</xs:keyref>
|
||||
<xs:keyref name="sliderLink" refer="uniqueID">
|
||||
<xs:selector xpath="slider"/>
|
||||
<xs:field xpath="@link"/>
|
||||
|
@@ -158,7 +158,7 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
||||
|
||||
Iterator<Attribute> attr;
|
||||
Iterator<Element> node;
|
||||
string type, name, val, defbtn;
|
||||
string type, name, val, defbtn, escbtn;
|
||||
|
||||
xml.FirstChildElement()->GetValue(&type);
|
||||
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);
|
||||
}
|
||||
defTextClr = clr;
|
||||
} else if(name == "defbtn") {
|
||||
}else if(name == "defbtn"){
|
||||
defbtn = val;
|
||||
}else if(name == "escbtn"){
|
||||
escbtn = val;
|
||||
}else if(name != "debug")
|
||||
throw xBadAttr(type,name,attr->Row(),attr->Column(),fname);
|
||||
}
|
||||
@@ -337,6 +339,8 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
||||
|
||||
// Set the default button.
|
||||
setDefaultButton(defbtn);
|
||||
// Set the escape button.
|
||||
setEscapeButton(escbtn);
|
||||
|
||||
// Sort by 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){
|
||||
ctrlIter iter = controls.begin();
|
||||
bool enterKeyHit = keyHit.spec && keyHit.k == key_enter;
|
||||
bool escapeKeyHit = keyHit.spec && keyHit.k == key_esc;
|
||||
while(iter != controls.end()){
|
||||
cControl* ctrl = iter->second;
|
||||
if(ctrl->isVisible()){
|
||||
if(ctrl->isClickable() &&
|
||||
(ctrl->getAttachedKey() == keyHit || (ctrl->isDefault() && enterKeyHit))){
|
||||
(ctrl->getAttachedKey() == keyHit || (ctrl->isDefault() && enterKeyHit) || (ctrl->isEscape() && escapeKeyHit))){
|
||||
|
||||
ctrl->handleKeyTriggered(*this);
|
||||
return;
|
||||
@@ -940,11 +945,6 @@ void cDialog::process_keystroke(cKey keyHit){
|
||||
}
|
||||
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){
|
||||
@@ -1157,21 +1157,38 @@ bool cDialog::hasControl(std::string id) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void cDialog::setDefaultButton(std::string defbtn) {
|
||||
if(!defbtn.empty() && !hasControl(defbtn)){
|
||||
void cDialog::setSpecialButton(std::string& name_ref, std::string name, bool escape) {
|
||||
if(!name.empty() && !hasControl(name)){
|
||||
// this is likely because the dialogxml is malformed. maybe the linter already checks this,
|
||||
// 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()){
|
||||
getControl(defaultButton).setDefault(false);
|
||||
if(!name_ref.empty()){
|
||||
if(escape){
|
||||
getControl(name_ref).setEscape(false);
|
||||
}else{
|
||||
getControl(name_ref).setDefault(false);
|
||||
}
|
||||
name_ref = "";
|
||||
}
|
||||
if(!defbtn.empty()){
|
||||
defaultButton = defbtn;
|
||||
getControl(defaultButton).setDefault(true);
|
||||
if(!name.empty()){
|
||||
name_ref = name;
|
||||
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$";
|
||||
|
||||
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 setDoAnimations(bool value) { doAnimations = value; }
|
||||
void setDefaultButton(std::string defbtn);
|
||||
void setEscapeButton(std::string escbtn);
|
||||
private:
|
||||
void setSpecialButton(std::string& name_ref, std::string name, bool escape);
|
||||
void draw();
|
||||
void handle_events();
|
||||
void handle_one_event(const sf::Event&, cFramerateLimiter& fps_limiter);
|
||||
@@ -277,6 +279,7 @@ private:
|
||||
boost::any result;
|
||||
std::string fname;
|
||||
std::string defaultButton;
|
||||
std::string escapeButton;
|
||||
sf::Clock animTimer, paintTimer;
|
||||
friend class cControl;
|
||||
friend class cContainer;
|
||||
|
@@ -172,6 +172,8 @@ public:
|
||||
std::string getAttachedKeyDescription() const;
|
||||
inline void setDefault(bool value) { isDefaultControl = value; }
|
||||
inline bool isDefault() { return isDefaultControl; }
|
||||
inline void setEscape(bool value) { isEscapeControl = value; }
|
||||
inline bool isEscape() { return isEscapeControl; }
|
||||
/// Attach an event handler to this control.
|
||||
/// @tparam t The type of event to attach.
|
||||
/// @param handler The event handler function or functor. Its signature depends on the event type.
|
||||
@@ -477,6 +479,8 @@ protected:
|
||||
cKey key;
|
||||
/// Whether the control is the default control of its dialog.
|
||||
bool isDefaultControl = false;
|
||||
/// Whether the control is the cancel control of its dialog.
|
||||
bool isEscapeControl = false;
|
||||
|
||||
/// Draw a frame around the control.
|
||||
/// @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
|
||||
other debug information.
|
||||
* `fore` - The default text colour. Generally this shouldn't be needed.
|
||||
* `defbtn` - The ID (`name` attribute) of the default button. This is an
|
||||
IDREF, so it must exist in the dialog.
|
||||
* `defbtn` - The ID (`name` attribute) of button triggered by Enter/Return.
|
||||
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
|
||||
----------------
|
||||
|
Reference in New Issue
Block a user