Refactor the dialog engine's event handling to make it easier to add new events in the future

- Focus event split into focus and defocus
- Scroll event added but not yet properly implemented

Also:
- Remove the useless (and ignored) clickable attributes from the schema
- Pave the way for controls other than fields to be able to recieve keyboard focus
This commit is contained in:
2015-10-03 19:49:33 -04:00
parent 547e78dc86
commit 8afd3825b0
25 changed files with 495 additions and 285 deletions

View File

@@ -137,7 +137,6 @@
<xs:attributeGroup ref="frame"/>
<xs:attributeGroup ref="font"/>
<xs:attribute ref="def-key"/>
<xs:attribute name="clickable" default="false" type="bool"/>
<xs:attribute name="fromlist" default="none" type="xs:string"/>
<xs:attributeGroup ref="rect-size"/>
</xs:complexType>
@@ -161,7 +160,6 @@
</xs:simpleType>
</xs:attribute>
<xs:attribute ref="def-key"/>
<xs:attribute name="clickable" default="false" type="bool"/>
<xs:attribute name="num" use="required" type="xs:integer"/>
<xs:attributeGroup ref="rect-size"/>
</xs:complexType>

View File

@@ -36,6 +36,7 @@
<ClInclude Include="..\..\dialogxml\control.hpp" />
<ClInclude Include="..\..\dialogxml\dialog.hpp" />
<ClInclude Include="..\..\dialogxml\dialog.keys.hpp" />
<ClInclude Include="..\..\dialogxml\dlogevt.hpp" />
<ClInclude Include="..\..\dialogxml\dlogutil.buttons.hpp" />
<ClInclude Include="..\..\dialogxml\dlogutil.hpp" />
<ClInclude Include="..\..\dialogxml\field.hpp" />

View File

@@ -130,6 +130,9 @@
<ClInclude Include="..\..\dialogxml\dialog.keys.hpp">
<Filter>DialogXML\Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\dialogxml\dlogevt.hpp">
<Filter>DialogXML\Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\dialogxml\dlogutil.buttons.hpp">
<Filter>DialogXML\Header Files</Filter>
</ClInclude>

View File

@@ -638,6 +638,7 @@
915325181A2E37EE000A9A1C /* specials_parse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = specials_parse.cpp; sourceTree = "<group>"; };
91597A6C1A3BED2D00BE7BF9 /* spell.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = spell.hpp; sourceTree = "<group>"; };
91597A6E1A3BEDC700BE7BF9 /* spell.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spell.cpp; sourceTree = "<group>"; };
915AF9E91BC04171008AEF49 /* dlogevt.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = dlogevt.hpp; sourceTree = "<group>"; };
915E09071A316D6A008BDF00 /* map_parse.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = map_parse.hpp; sourceTree = "<group>"; };
915E09081A316D89008BDF00 /* map_parse.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = map_parse.cpp; sourceTree = "<group>"; };
9169C31B1B37A5D50041002B /* Blades of Exile.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Blades of Exile.app"; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -935,6 +936,7 @@
910BBA3B0FB8DA8E001E34EA /* control.hpp */,
910BBA170FB8BECA001E34EA /* dialog.hpp */,
918D59A718EA513900735B66 /* dialog.keys.hpp */,
915AF9E91BC04171008AEF49 /* dlogevt.hpp */,
91A32BD10FDB797B00C4E957 /* dlogutil.buttons.hpp */,
910BBAD90FB91D2A001E34EA /* dlogutil.hpp */,
910BBAB40FB91A26001E34EA /* field.hpp */,

View File

@@ -23,19 +23,6 @@
extern sf::Texture bg_gworld;
void cButton::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported){
throw xHandlerNotSupported(true);
}
void cButton::attachClickHandler(click_callback_t f) throw(){
onClick = f;
}
bool cButton::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
if(onClick) return onClick(me,id,mods);
return false;
}
cButton::cButton(sf::RenderWindow& parent) :
cControl(CTRL_BTN,parent),
wrapLabel(false),
@@ -59,6 +46,14 @@ bool cButton::isClickable(){
return true;
}
bool cButton::isFocusable(){
return false;
}
bool cButton::isScrollable(){
return false;
}
void cButton::draw(){
rectangle from_rect, to_rect;
@@ -333,41 +328,28 @@ cLed::cLed(cDialog& parent) :
textFont(FONT_BOLD),
textSize(10) {
type = BTN_LED;
using namespace std::placeholders;
attachEventHandler<EVT_CLICK>(std::bind(&cLed::defaultClickHandler, this, _1, _2, _3));
}
void cLed::attachClickHandler(click_callback_t f) throw(){
onClick = f;
}
void cLed::attachFocusHandler(focus_callback_t f) throw(){
onFocus = f;
}
bool cLed::triggerFocusHandler(cDialog& me, std::string id, bool losing){
if(onFocus != nullptr) return onFocus(me,id,losing);
return true;
}
bool cLed::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
bool result;
eLedState oldState = state;
if(onClick != nullptr) result = onClick(me,id,mods);
else{ // simple state toggle
switch(state){
case led_red:
case led_green:
state = led_off;
break;
case led_off:
state = led_red;
}
result = true;
void cLed::defaultClickHandler(cDialog&, std::string, eKeyMod) {
// simple state toggle
switch(state){
case led_red:
case led_green:
state = led_off;
break;
case led_off:
state = led_red;
}
}
void cLed::callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) {
eLedState oldState = state;
if(onClick) onClick(me,id,mods);
if(!triggerFocusHandler(me,id, oldState != led_off)){
result = false;
state = oldState;
}
return result;
}
void cLed::setFormat(eFormat prop, short val) throw(xUnsupportedProp){
@@ -453,14 +435,6 @@ void cLedGroup::recalcRect(){
frame.inset(-6,-6);
}
void cLedGroup::attachClickHandler(click_callback_t f) throw() {
onClick = f;
}
void cLedGroup::attachFocusHandler(focus_callback_t f) throw() {
onFocus = f;
}
void cLed::setState(eLedState to){
state = to;
}
@@ -594,17 +568,17 @@ bool cLedGroup::handleClick(location where) {
return true;
}
bool cLedGroup::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
void cLedGroup::callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) {
std::string which_clicked = clicking;
clicking = "";
if(choices[which_clicked]->triggerClickHandler(me,which_clicked,mods)){
if(onClick && !onClick(me,id,mods)) return false;
if(onClick) onClick(me,id,mods);
if(!curSelect.empty()) {
choices[curSelect]->setState(led_off);
if(!choices[curSelect]->triggerFocusHandler(me,curSelect,true)){
choices[curSelect]->setState(led_red);
return false;
return;
}
}
choices[which_clicked]->setState(led_red);
@@ -612,9 +586,9 @@ bool cLedGroup::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
if(!curSelect.empty())
choices[curSelect]->setState(led_red);
choices[which_clicked]->setState(led_off);
return false;
return;
}
}else return false;
}else return;
std::string savePrevSelect = prevSelect;
prevSelect = curSelect;
@@ -625,14 +599,20 @@ bool cLedGroup::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
choices[which_clicked]->setState(led_off);
curSelect = prevSelect;
prevSelect = savePrevSelect;
return false;
return;
}
return true;
return;
}
bool cLedGroup::triggerFocusHandler(cDialog& me, std::string id, bool losingFocus){
if(onFocus != nullptr) return onFocus(me,id,losingFocus);
return true;
void cLedGroup::attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) throw(xHandlerNotSupported) {
if(!f) {
attachEventHandler<EVT_FOCUS>(nullptr);
return;
}
using namespace std::placeholders;
attachEventHandler<EVT_FOCUS>([f](cDialog& me, std::string id) {
f(me, id, false);
});
}
void cLedGroup::disable(std::string /*id*/) {
@@ -676,6 +656,14 @@ bool cLedGroup::isClickable(){
return true;
}
bool cLedGroup::isScrollable(){
return false;
}
bool cLedGroup::isFocusable(){
return false;
}
bool cLedGroup::hasChild(std::string id) {
return choices.find(id) != choices.end();
}
@@ -800,6 +788,11 @@ void cLedGroup::restore(storage_t to) {
else setSelected("");
}
void cLedGroup::forEach(std::function<void(std::string,cControl&)> callback) {
for(auto ctrl : choices)
callback(ctrl.first, *ctrl.second);
}
std::string cLedGroup::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
Iterator<Attribute> attr;

View File

@@ -51,10 +51,6 @@ public:
/// @copydoc cDialog::init()
static void init();
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw();
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
//virtual void setPict(short pict, short type) = 0;
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp);
@@ -72,15 +68,21 @@ public:
/// @param parent The parent window
explicit cButton(sf::RenderWindow& parent);
bool isClickable();
bool isFocusable();
bool isScrollable();
virtual ~cButton();
void draw();
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK};
}
cButton& operator=(cButton& other) = delete;
cButton(cButton& other) = delete;
protected:
/// The type of button.
eBtnType type;
/// The click handler.
click_callback_t onClick;
/// Construct a new button.
/// @param parent The parent dialog.
/// @param t The type of control. Should be either CTRL_LED or CTRL_BTN.
@@ -113,10 +115,6 @@ public:
/// @return true to indicate the event should continue.
static bool noAction(cDialog&,std::string,eKeyMod) {return true;}
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw();
void attachFocusHandler(focus_callback_t f) throw();
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
storage_t store();
@@ -132,14 +130,21 @@ public:
/// @return The current state.
eLedState getState();
void draw();
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK, EVT_FOCUS, EVT_DEFOCUS};
}
cLed& operator=(cLed& other) = delete;
cLed(cLed& other) = delete;
private:
void defaultClickHandler(cDialog&, std::string, eKeyMod);
void callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) override;
eLedState state;
eFont textFont;
short textSize;
static rectangle ledRects[3][2];
focus_callback_t onFocus;
};
/// A group of LED buttons that behave like radio buttons.
@@ -161,30 +166,29 @@ private:
/// However, when the focus handler of the LED group is called, the selection _has_ been updated.,
/// so getSelected() will return the new selection. (This is the reason for the getPreviousSelection() method.)
class cLedGroup : public cContainer {
click_callback_t onClick;
focus_callback_t onFocus;
bool drawFramed;
bool drawFramed = false;
std::map<std::string,cLed*> choices;
std::string fromList;
std::string curSelect, prevSelect;
std::string clicking;
void callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) override;
cLedGroup& operator=(cLedGroup& other) = delete;
cLedGroup(cLedGroup& other) = delete;
public:
/// @deprecated in favour of @ref attachEventHandler
void attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) throw(xHandlerNotSupported);
std::string parse(ticpp::Element& who, std::string fname);
/// @copydoc cControl::attachClickHandler()
///
/// The click handler is called whenever an LED in the group is clicked, even if it's the currently selected LED.
void attachClickHandler(click_callback_t f) throw();
/// @copydoc cControl::attachFocusHandler()
///
/// An LED group triggers focus handlers when a choice other than the currently selected one is clicked.
/// The third parameter is always false for an LED group's focus handler.
/// You can determine what changed using getPrevSelection() and getSelected(), and can do whatever post-processing
/// you want, including selecting a completely different option.
void attachFocusHandler(focus_callback_t f) throw();
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK, EVT_FOCUS};
}
storage_t store();
void restore(storage_t to);
/// Add a new LED to this group.
@@ -214,6 +218,8 @@ public:
/// @param parent The parent dialog.
explicit cLedGroup(cDialog& parent);
bool isClickable();
bool isFocusable();
bool isScrollable();
bool handleClick(location where);
virtual ~cLedGroup();
/// Get one of the LEDs in this group.
@@ -239,6 +245,7 @@ public:
/// Call this after adding choices to the group to ensure that the choice is within the bounding rect.
/// If a choice is not within the bounding rect, it will not respond to clicks.
void recalcRect();
void forEach(std::function<void(std::string,cControl&)> callback) override;
/// A convenience type for making an iterator into the choice map.
typedef std::map<std::string,cLed*>::iterator ledIter;
void draw();

View File

@@ -37,15 +37,19 @@ void cControl::relocate(location to) {
frame.offset(to.x - frame.left, to.y - frame.top);
}
const char* xHandlerNotSupported::focusMsg = "This control cannot handle focus events.\n";
const char* xHandlerNotSupported::clickMsg = "This control cannot handle click events.\n";
const char* xHandlerNotSupported::msg[4] = {
"This control cannot handle click events.\n",
"This control cannot handle focus events.\n",
"This control cannot handle defocus events.\n",
"This control cannot handle scroll events.\n",
};
xHandlerNotSupported::xHandlerNotSupported(bool isFocus){
this->isFocus = isFocus;
xHandlerNotSupported::xHandlerNotSupported(eDlogEvt t){
this->evt = t;
}
const char* xHandlerNotSupported::what() const throw() {
if(isFocus) return focusMsg;
else return clickMsg;
assert("A handler not supported message is missing!" && evt < 4);
return msg[evt];
}
xUnsupportedProp::xUnsupportedProp(eFormat prop) throw(){
@@ -268,11 +272,38 @@ cControl::cControl(eControlType t, cDialog& p) : parent(&p), inWindow(&p.win), t
cControl::cControl(eControlType t, sf::RenderWindow& p) : parent(nullptr), inWindow(&p), type(t), visible(true), key({false, 0, mod_none}), frameStyle(FRM_INSET) {}
bool cControl::triggerClickHandler(cDialog&, std::string, eKeyMod){
void cControl::attachClickHandler(std::function<bool(cDialog&,std::string,eKeyMod)> f) throw(xHandlerNotSupported) {
if(!f) {
attachEventHandler<EVT_CLICK>(nullptr);
return;
}
attachEventHandler<EVT_CLICK>([f](cDialog& me, std::string id, eKeyMod mods) {
f(me, id, mods);
});
}
void cControl::attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) throw(xHandlerNotSupported) {
if(!f) {
attachEventHandler<EVT_FOCUS>(nullptr);
attachEventHandler<EVT_DEFOCUS>(nullptr);
return;
}
using namespace std::placeholders;
attachEventHandler<EVT_FOCUS>([f](cDialog& me, std::string id) {
f(me, id, false);
});
attachEventHandler<EVT_DEFOCUS>(std::bind(f, _1, _2, true));
}
bool cControl::triggerClickHandler(cDialog& dlg, std::string id, eKeyMod mods){
triggerEvent<EVT_CLICK>(dlg, id, mods);
return true;
}
bool cControl::triggerFocusHandler(cDialog&, std::string, bool){
bool cControl::triggerFocusHandler(cDialog& dlg, std::string id, bool losing){
if(losing) return triggerEvent<EVT_DEFOCUS>(dlg, id);
triggerEvent<EVT_FOCUS>(dlg, id);
return true;
}

View File

@@ -17,23 +17,19 @@
#include <string>
#include <exception>
#include <functional>
#include <set>
#include <boost/any.hpp>
#include "dialog.hpp"
#include "location.hpp"
//struct cPict {
// short pict;
// short type;
//};
/// Formatting properties
enum eFormat {
TXT_FRAME, ///< Whether to draw a frame around the control. Should be a boolean (true or false).
TXT_FONT, ///< The control's text font. Should be one of the constants FONT_PLAIN, FONT_BOLD, FONT_DUNGEON, FONT_MAIDEN.
TXT_SIZE, ///< The control's text size. Should be an integer indicating point size.
TXT_WRAP, ///< Whether the control should wrap. Should be a boolean (true or false).
TXT_FRAMESTYLE, ///< The control's frame style. Should be an enum from @a eFrameStyle.
TXT_FRAMESTYLE, ///< The control's frame style. Should be an enum from @ref eFrameStyle.
};
/// Frame styles
@@ -58,20 +54,14 @@ enum eControlType {
CTRL_PANE, ///< A scroll pane
};
/// The signature of a click handler.
typedef std::function<bool(cDialog&,std::string,eKeyMod)> click_callback_t;
/// The signature of a focus handler.
typedef std::function<bool(cDialog&,std::string,bool)> focus_callback_t;
/// Thrown when you try to set a handler that the control does not support.
class xHandlerNotSupported : public std::exception {
static const char* focusMsg;
static const char* clickMsg;
bool isFocus;
static const char* msg[4];
eDlogEvt evt;
public:
/// Construct a new exception.
/// @param isFocus true to indicate a focus event, false for a click event.
xHandlerNotSupported(bool isFocus);
/// @param t The type of event.
xHandlerNotSupported(eDlogEvt t);
/// @return The error message.
const char* what() const throw();
};
@@ -117,37 +107,72 @@ public:
/// @return the currently-assigned keyboard shortcut.
/// @note You should first check that a shortcut is assigned using hasKey().
cKey getAttachedKey();
/// 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.
/// @return The previous handler that has been overridden, if any.
/// @throw xHandlerNotSupported if the event type is not supported by this control.
/// @note Only one handler can be set at a time for any given event. To remove a handler, set it to nullptr.
template<eDlogEvt t> typename event_fcn<t>::type attachEventHandler(typename event_fcn<t>::type handler) {
if(getSupportedHandlers().count(t) == 0) throw xHandlerNotSupported(t);
auto old_handler = event_handlers[t];
if(handler) event_handlers[t] = handler;
else event_handlers[t].clear();
if(old_handler.empty()) return nullptr;
return boost::any_cast<typename event_fcn<t>::type>(old_handler);
}
/// Attach a click handler to this control.
/// @param f The click handler to attach.
/// @throw xHandlerNotSupported if this control does not support click handlers. Most controls do support click handlers.
/// @deprecated in favour of @ref attachEventHandler
/// @note Only one click handler can be set at a time. To remove the click handler, set it to null.
///
/// A click handler must be able to accept three parameters: a reference to the containing dialog, the unique key of the
/// clicked item, and a representation of any modifier keys that are currently held.
virtual void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) = 0;
virtual void attachClickHandler(std::function<bool(cDialog&,std::string,eKeyMod)> f) throw(xHandlerNotSupported);
/// Attach a focus handler to this control.
/// @param f The focus handler to attach.
/// @throw xHandlerNotSupported if this control does not support focus handlers. Most controls do not support focus handlers.
/// @deprecated in favour of @ref attachEventHandler
/// @note Only one focus handler can be set at a time. To remove the focus handler, set it to null.
///
/// A focus handler must be able to accept three parameters: a reference to the containing dialog, the unique key of the
/// clicked item, and a boolean indicating whether focus is being lost or gained; a value of true indicates that
/// focus is being lost, while false indicates it's being gained. Most handlers will only need to act when the
/// third parameter is true. If the handler returns false, the focus change is cancelled.
virtual void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported) = 0;
virtual void attachFocusHandler(std::function<bool(cDialog&,std::string,bool)> f) throw(xHandlerNotSupported);
/// Trigger an event on this control.
/// @tparam t The type of event to trigger.
/// @tparam Params Additional parameters, depending on the event type.
/// @param dlg The parent dialog of this control.
/// @param args Additional arguments, depending on the event type.
/// @return The result of the event handler. If there was no handler, and the handler would've returned a bool,
/// then true is returned. This also applies if the event type is not supported by the control.
template<eDlogEvt t, typename... Params> typename event_fcn<t>::type::result_type triggerEvent(cDialog& dlg, Params... args) {
using fcn_t = typename event_fcn<t>::type;
if(event_handlers[t].empty())
return callHandler(fcn_t(), dlg, args...);
auto handler = boost::any_cast<fcn_t>(event_handlers[t]);
return callHandler(handler, dlg, args...);
}
/// Check if a handler is assigned for a given event.
/// @param t The type of event to check for.
/// @return True if one is assigned, false otherwise.
bool haveHandler(eDlogEvt t) {
return !event_handlers[t].empty();
}
/// Trigger the click handler for this control.
/// @param me A reference to the current dialog.
/// @param id The unique key of this control.
/// @param mods The currently-held keyboard modifiers.
/// @return true if the event should continue, false if it should be cancelled.
virtual bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
virtual bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods) final;
/// Trigger the focus handler for this control.
/// @param me A reference to the current dialog.
/// @param id The unique key of this control.
/// @param losingFocus true if this control is losing focus, false if it is gaining focus.
/// @return true if the event should continue, false if it should be cancelled.
virtual bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
//virtual void setPict(short pict, short type) = 0;
virtual bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus) final;
/// Make this control visible.
virtual void show(); // cd_activate_item true
/// Make this control invisible.
@@ -157,6 +182,8 @@ public:
bool isVisible(); // cd_get_active
/// Check if this control is a container which contains other controls.
/// @return true if it's a container
/// @note Generally you shouldn't override this. If you need a container, then
/// extend @ref cContainer instead of cControl.
virtual bool isContainer() {return false;}
/// Set if this control is active. A control is normally active when the mouse button is held down within its bounding rect.
/// @param active true if it should be active, false if not
@@ -209,6 +236,14 @@ public:
/// In fact, some controls return true only if a click handler is assigned.
/// Others, like editable text fields, are clickable but do not support click handlers.
virtual bool isClickable() = 0;
/// Check if the control is focusable.
/// @return true if it's focusable.
/// @note This does not indicate whether the control supports focus and defocus handlers.
virtual bool isFocusable() = 0;
/// Check if the control is scrollable.
/// @return true if it's scrollable.
/// @note This does not indicate whether the control supports scroll handlers.
virtual bool isScrollable() = 0;
/// Handles the action of clicking this control.
/// @param where The exact location at which the mouse was pressed, relative to the dialog.
/// @return true if the click was successful; false if it was cancelled.
@@ -243,6 +278,54 @@ public:
cControl& operator=(cControl& other) = delete;
cControl(cControl& other) = delete;
protected:
/// Returns a list of event handlers that this control supports.
/// @return The list of handlers as a std::set.
///
/// See the documentation of this method in subclasses for explanations of what handlers
/// each control supports and how those handlers work for that control.
virtual const std::set<eDlogEvt> getSupportedHandlers() const = 0;
/// Called when a click event is triggered on this control. Override this to
/// customize the behaviour of clicks on the control. Note that, if you override it, you are
/// responsible for calling the passed handler, if it exists. (Test for existance with operator!.)
/// @param handler The handler that should be triggered as a result of this click.
/// @param dlg The dialog in which the event was triggered; intended to be passed to the handler.
/// @param id The ID of the control that triggered the event; intended to be passed to the handler.
/// @param mods The key modifiers currently pressed; intended to be passed to the handler.
virtual void callHandler(event_fcn<EVT_CLICK>::type handler, cDialog& dlg, std::string id, eKeyMod mods) {
if(handler) handler(dlg, id, mods);
}
/// Called when a focus event is triggered on this control. Override this to
/// customize the behaviour of focusing on the control. Note that, if you override it, you are
/// responsible for calling the passed handler, if it exists. (Test for existance with operator!.)
/// @param handler The handler that should be triggered as a result of this focus.
/// @param dlg The dialog in which the event was triggered; intended to be passed to the handler.
/// @param id The ID of the control that triggered the event; intended to be passed to the handler.
virtual void callHandler(event_fcn<EVT_FOCUS>::type handler, cDialog& dlg, std::string id) {
if(handler) handler(dlg, id);
}
/// Called when a defocus event is triggered on this control. Override this to
/// customize the behaviour of defocusing on the control. Note that, if you override it, you are
/// responsible for calling the passed handler, if it exists. (Test for existance with operator!.)
/// @param handler The handler that should be triggered as a result of this defocus.
/// @param dlg The dialog in which the event was triggered; intended to be passed to the handler.
/// @param id The ID of the control that triggered the event; intended to be passed to the handler.
/// @return Whether the defocus should be allowed to happen.
virtual bool callHandler(event_fcn<EVT_DEFOCUS>::type handler, cDialog& dlg, std::string id) {
if(handler) return handler(dlg, id);
return true;
}
/// Called when a scroll event is triggered on this control. Override this to
/// customize the behaviour of scrolling on the control. Note that, if you override it, you are
/// responsible for calling the passed handler, if it exists. (Test for existance with operator!.)
/// @param handler The handler that should be triggered as a result of this scroll.
/// @param dlg The dialog in which the event was triggered; intended to be passed to the handler.
/// @param id The ID of the control that triggered the event; intended to be passed to the handler.
/// @param newVal The new value of the scrollbar.
/// @return Whether the scrollbar should be allowed to scroll to this point.
virtual bool callHandler(event_fcn<EVT_SCROLL>::type handler, cDialog& dlg, std::string id, int newVal) {
if(handler) return handler(dlg, id, newVal);
return true;
}
/// Parses an HTML colour code.
/// Recognizes three-digit hex, six-digit hex, and HTML colour names.
/// @param code The colour code to parse.
@@ -282,9 +365,13 @@ protected:
private:
friend class cDialog; // TODO: This is only so it can access parseColour... hack!
eControlType type;
std::map<eDlogEvt, boost::any> event_handlers;
};
/// A superclass to represent a control that contains other controls.
class cContainer : public cControl {
void callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) override;
std::string clicking;
public:
/// Create a new container control attached to an arbitrary window, rather than a dialog.
/// @param t The type of the control.
@@ -303,9 +390,23 @@ public:
/// @throw std::invalid_argument if the control does not exist.
/// @return a reference to the requested control.
virtual cControl& getChild(std::string id) = 0;
/// Executes a function for every control in this container.
/// @param callback A function taking a string as its first argument
/// and a control reference as its second argument.
virtual void forEach(std::function<void(std::string,cControl&)> callback) = 0;
/// @copydoc getChild()
cControl& operator[](std::string id) {return getChild(id);}
bool isContainer() {return true;}
bool handleClick(location where);
};
// This is defined here instead of in dialog.hpp because it needs cControl to be complete.
/// @note You need to include control.hpp to use this.
template<eDlogEvt t> void cDialog::attachEventHandlers(typename event_fcn<t>::type handler, const std::vector<std::string>& controls) {
cDialog& me = *this;
for(std::string control : controls) {
me[control].attachEventHandler<t>(handler);
}
}
#endif

View File

@@ -508,9 +508,7 @@ void cDialog::run(std::function<void(cDialog&)> onopen){
key.mod += mod_ctrl;
if(currentEvent.key.shift) key.mod += mod_shift;
if(currentEvent.key.alt) key.mod += mod_alt;
itemHit = process_keystroke(key);
if(!itemHit.empty())
where = controls[itemHit]->getBounds().centre();
process_keystroke(key);
// Now check for focused fields.
if(currentFocus.empty()) break;
// If it's a tab, handle tab order
@@ -547,7 +545,7 @@ void cDialog::run(std::function<void(cDialog&)> onopen){
if(kb::isKeyPressed(kb::LShift)) key.mod += mod_shift;
if(kb::isKeyPressed(kb::RShift)) key.mod += mod_shift;
where = {currentEvent.mouseButton.x, currentEvent.mouseButton.y};
itemHit = process_click(where);
process_click(where, key.mod);
break;
default: // To silence warning of unhandled enum values
break;
@@ -564,11 +562,6 @@ void cDialog::run(std::function<void(cDialog&)> onopen){
if(!inField) set_cursor(sword_curs);
break;
}
if(itemHit.empty()) continue;;
ctrlIter ctrl = controls.find(itemHit);
// TODO: Should it do something with the boolean return value?
if(ctrl != controls.end()) ctrl->second->triggerClickHandler(*this,itemHit,key.mod);
itemHit.clear();
}
win.setVisible(false);
while(parentWin->pollEvent(currentEvent));
@@ -589,7 +582,7 @@ template<typename Iter> void cDialog::handleTabOrder(string& itemHit, Iter begin
while(iter != cur){
// If tab order is explicitly specified for all fields, gaps are possible
if(iter->second == nullptr) continue;
if(iter->second->getType() == CTRL_FIELD && iter->second->isVisible()){
if(iter->second->isFocusable() && iter->second->isVisible()){
if(iter->second->triggerFocusHandler(*this,iter->first,false)){
currentFocus = iter->first;
}
@@ -660,14 +653,14 @@ cTextField* cDialog::getFocus() {
return dynamic_cast<cTextField*>(iter->second);
}
void cDialog::attachClickHandlers(click_callback_t handler, std::vector<std::string> controls) {
void cDialog::attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyMod)> handler, std::vector<std::string> controls) {
cDialog& me = *this;
for(std::string control : controls) {
me[control].attachClickHandler(handler);
}
}
void cDialog::attachFocusHandlers(focus_callback_t handler, std::vector<std::string> controls) {
void cDialog::attachFocusHandlers(std::function<bool(cDialog&,std::string,bool)> handler, std::vector<std::string> controls) {
cDialog& me = *this;
for(std::string control : controls) {
me[control].attachFocusHandler(handler);
@@ -716,7 +709,7 @@ bool cDialog::addLabelFor(std::string key, std::string label, eLabelPos where, s
return add(labelCtrl, labelRect, key);
}
std::string cDialog::process_keystroke(cKey keyHit){
void cDialog::process_keystroke(cKey keyHit){
ctrlIter iter = controls.begin();
while(iter != controls.end()){
if(iter->second->isVisible() && iter->second->isClickable() && iter->second->getAttachedKey() == keyHit){
@@ -732,31 +725,32 @@ std::string cDialog::process_keystroke(cKey keyHit){
iter->second->setActive(false);
draw();
sf::sleep(sf::milliseconds(8));
return iter->first;
iter->second->triggerClickHandler(*this,iter->first,mod_none);
return;
}
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;
return process_keystroke(keyHit);
process_keystroke(keyHit);
}
return "";
}
std::string cDialog::process_click(location where){
void cDialog::process_click(location where, eKeyMod mods){
// TODO: Return list of all controls whose bounding rect contains the clicked point.
// Then the return value of the click handler can mean "Don't pass this event on to other things below me".
ctrlIter iter = controls.begin();
while(iter != controls.end()){
if(iter->second->isVisible() && iter->second->isClickable() && where.in(iter->second->getBounds())){
if(iter->second->handleClick(where))
return iter->first;
else return "";
break;
else return;
}
iter++;
}
return "";
if(iter != controls.end())
iter->second->triggerClickHandler(*this,iter->first,mods);
}
xBadNode::xBadNode(std::string t, int r, int c, std::string dlg) throw() :

View File

@@ -22,6 +22,7 @@
#include "ticpp.h"
#include "dialog.keys.hpp"
#include "dlogevt.hpp"
#include "location.hpp"
#include <boost/any.hpp>
@@ -161,6 +162,12 @@ public:
/// Recalculate the dialog's bounding rect.
/// Call this after adding controls to the dialog to ensure that the control is within the bounding rect.
void recalcRect();
/// Attach the same handler for a given event to several controls.
/// @tparam t The type of event to attach handlers for.
/// @param controls A list of control IDs to attach the handlers to.
/// @throw xHandlerNotSupported if any of the controls do not support this event type.
/// In this event, any subsequent controls in the list will not have had the handlers attached.
template<eDlogEvt t> void attachEventHandlers(typename event_fcn<t>::type handler, const std::vector<std::string>& controls);
// TODO: It seems like a bad thing for these two to not use the typedefs...
/// Attach the same click handler to several controls.
/// @param handler The handler to attach.
@@ -168,6 +175,7 @@ public:
/// @throw xHandlerNotSupported if any of the controls do not support click handlers.
/// @throw std::invalid_argument if any of the controls do not exist.
/// @see cControl::attachClickHandler()
/// @deprecated in favour of @ref attachEventHandlers
void attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyMod)> handler, std::vector<std::string> controls);
/// Attach the same focus handler to several controls.
/// @param handler The handler to attach.
@@ -175,6 +183,7 @@ public:
/// @throw xHandlerNotSupported if any of the controls do not support focus handlers.
/// @throw std::invalid_argument if any of the controls do not exist.
/// @see cControl::attachFocusHandler()
/// @deprecated in favour of @ref attachEventHandlers
void attachFocusHandlers(std::function<bool(cDialog&,std::string,bool)> handler, std::vector<std::string> controls);
/// Get the bounding rect of the dialog.
/// @return The dialog's bounding rect.
@@ -205,8 +214,8 @@ public:
cDialog(cDialog& other) = delete;
private:
void draw();
std::string process_keystroke(cKey keyHit);
std::string process_click(location where);
void process_keystroke(cKey keyHit);
void process_click(location where, eKeyMod mods);
bool dialogNotToast, didAccept;
rectangle winRect;
boost::any result;

71
src/dialogxml/dlogevt.hpp Normal file
View File

@@ -0,0 +1,71 @@
//
// dlogevt.hpp
// BoE
//
// Created by Celtic Minstrel on 15-10-03.
//
//
#ifndef BoE_dlogevt_hpp
#define BoE_dlogevt_hpp
#include <functional>
#include <string>
#include <type_traits>
#include "dialog.keys.hpp"
/// Represents an event that can occur in a dialog.
enum eDlogEvt {
EVT_CLICK, ///< A click event - the mouse has been pressed and released.
EVT_FOCUS, ///< A focus event - a control has gained focus.
EVT_DEFOCUS, ///< A defocus event - a control is losing focus.
EVT_SCROLL, ///< A scroll event - a control has changed in response to the mouse wheel.
};
/// A metafunction to obtain the signature of a handler for a given event.
/// The first parameter is always cDialog& and the second paramater is
/// usually std::string. Return type and additional parameters, if any,
/// may vary.
/// @tparam t The type of event you need a handler for.
/// @return An std::function type representing the even handler
/// as member type.
template<eDlogEvt t> struct event_fcn;
class cDialog;
/// @typedef type
/// @memberof event_fcn
/// @brief The return value; an std::function instantiation.
/// Specialization of event_fcn for EVT_CLICK.
template<> struct event_fcn<EVT_CLICK> {
/// The signature for a click event handler.
/// Requires the dialog, the ID of the clicked element, and the current modifier key state.
/// Returns nothing.
using type = std::function<void(cDialog&, std::string, eKeyMod)>;
};
/// Specialization of event_fcn for EVT_FOCUS.
template<> struct event_fcn<EVT_FOCUS> {
/// The signature for a focus event handler.
/// Requires only the dialog and the ID of the clicked element.
/// Returns nothing.
using type = std::function<void(cDialog&, std::string)>;
};
/// Specialization of event_fcn for EVT_DEFOCUS.
template<> struct event_fcn<EVT_DEFOCUS> {
/// The signature of a defocus event handler.
/// Requires only the dialog and the ID of the clicked element.
/// Returns false if defocus should be cancelled.
using type = std::function<bool(cDialog&, std::string)>;
};
/// Specialization of event_fcn for EVT_SCROLL.
template<> struct event_fcn<EVT_SCROLL> {
/// The signature of a scroll event handler.
/// Requires the dialog, the ID of the clicked element, and the resulting value.
/// Returns false if scrolling should be cancelled.
using type = std::function<bool(cDialog&, std::string, int)>;
};
#endif

View File

@@ -17,16 +17,8 @@
#include "winutil.hpp"
#include "cursors.hpp"
void cTextField::attachClickHandler(click_callback_t) throw(xHandlerNotSupported){
throw xHandlerNotSupported(false);
}
void cTextField::attachFocusHandler(focus_callback_t f) throw(){
onFocus = f;
}
bool cTextField::triggerFocusHandler(cDialog& me, std::string id, bool losingFocus){
if(losingFocus && field_type != FLD_TEXT) {
bool cTextField::callHandler(event_fcn<EVT_DEFOCUS>::type onFocus, cDialog& me, std::string id) {
if(field_type != FLD_TEXT) {
try {
std::string contents = getText();
switch(field_type) {
@@ -52,13 +44,20 @@ bool cTextField::triggerFocusHandler(cDialog& me, std::string id, bool losingFoc
}
}
bool passed = true;
if(onFocus != nullptr) passed = onFocus(me,id,losingFocus);
if(passed) haveFocus = !losingFocus;
if(onFocus) passed = onFocus(me,id);
if(passed) haveFocus = false;
if(haveFocus && insertionPoint < 0)
insertionPoint = getText().length();
return passed;
}
void cTextField::callHandler(event_fcn<EVT_FOCUS>::type onFocus, cDialog& me, std::string id) {
if(onFocus) onFocus(me,id);
haveFocus = true;
if(insertionPoint < 0)
insertionPoint = getText().length();
}
void cTextField::setText(std::string to) {
cControl::setText(to);
if(haveFocus)
@@ -193,6 +192,14 @@ bool cTextField::isClickable(){
return true;
}
bool cTextField::isFocusable(){
return true;
}
bool cTextField::isScrollable(){
return false;
}
bool cTextField::hasFocus() {
return haveFocus;
}

View File

@@ -34,11 +34,13 @@ enum eFldType {
class cTextField : public cControl {
public:
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
/// @copydoc cControl::attachFocusHandler()
/// For text fields, this is triggered when it loses or gains the input focus.
void attachFocusHandler(focus_callback_t f) throw();
bool triggerFocusHandler(cDialog& me, std::string id, bool losingFocus);
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_FOCUS, EVT_DEFOCUS};
}
bool handleClick(location where);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
@@ -57,6 +59,8 @@ public:
/// @param parent The parent dialog.
explicit cTextField(cDialog& parent);
bool isClickable();
bool isFocusable();
bool isScrollable();
virtual ~cTextField();
void draw();
/// Check if this text field currently has input focus.
@@ -70,11 +74,12 @@ public:
/// This field is only used by cDialog during the loading process. Changing it will have no effect.
long tabOrder = 0;
private:
void callHandler(event_fcn<EVT_FOCUS>::type onFocus, cDialog& me, std::string id) override;
bool callHandler(event_fcn<EVT_DEFOCUS>::type onFocus, cDialog& me, std::string id) override;
void set_ip(location clickLoc, int cTextField::* insertionPoint);
cUndoList history;
action_ptr current_action;
eFldType field_type;
focus_callback_t onFocus;
bool haveFocus;
int insertionPoint;
int selectionPoint;

View File

@@ -13,20 +13,6 @@
extern sf::Texture bg_gworld;
void cTextMsg::attachClickHandler(click_callback_t f) throw(){
onClick = f;
clickable = onClick != nullptr;
}
void cTextMsg::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported){
throw xHandlerNotSupported(true);
}
bool cTextMsg::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
if(onClick != nullptr) return onClick(me,id,mods);
return false;
}
void cTextMsg::setColour(sf::Color clr) throw(xUnsupportedProp) {
color = clr;
}
@@ -179,7 +165,6 @@ cTextMsg::cTextMsg(cDialog& parent) :
textFont(FONT_BOLD),
textSize(10),
color(parent.getDefTextClr()),
clickable(false),
fromList("none") {}
cTextMsg::cTextMsg(sf::RenderWindow& parent) :
@@ -188,11 +173,18 @@ cTextMsg::cTextMsg(sf::RenderWindow& parent) :
textFont(FONT_BOLD),
textSize(10),
color(cDialog::defaultBackground == cDialog::BG_DARK ? sf::Color::White : sf::Color::Black),
clickable(false),
fromList("none") {}
bool cTextMsg::isClickable(){
return clickable;
return haveHandler(EVT_CLICK);
}
bool cTextMsg::isFocusable(){
return false;
}
bool cTextMsg::isScrollable(){
return false;
}
void cTextMsg::draw(){
@@ -206,7 +198,7 @@ void cTextMsg::draw(){
style.pointSize = textSize;
if(drawFramed) drawFrame(2,frameStyle);
sf::Color draw_color = color;
if(clickable && depressed){
if(depressed){
draw_color.r = 256 - draw_color.r;
draw_color.g = 256 - draw_color.g;
draw_color.b = 256 - draw_color.b;

View File

@@ -23,9 +23,6 @@
class cTextMsg : public cControl {
public:
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw();
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp);
@@ -37,16 +34,23 @@ public:
/// @param parent The parent window.
explicit cTextMsg(sf::RenderWindow& parent);
bool isClickable();
bool isFocusable();
bool isScrollable();
virtual ~cTextMsg();
void draw();
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK};
}
cTextMsg& operator=(cTextMsg& other) = delete;
cTextMsg(cTextMsg& other) = delete;
private:
bool drawFramed, clickable;
bool drawFramed;
short textSize;
eFont textFont;
sf::Color color;
std::string fromList;
click_callback_t onClick;
};
#endif

View File

@@ -70,25 +70,6 @@ std::map<ePicType,void(cPict::*)(short,rectangle)>& cPict::drawPict(){
return f;
}
void cPict::attachClickHandler(click_callback_t f) throw(){
if(f == nullptr){
onClick = nullptr;
clickable = false;
}else{
onClick = f;
clickable = true;
}
}
void cPict::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported){
throw xHandlerNotSupported(true);
}
bool cPict::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods){
if(onClick != nullptr) return onClick(me,id,mods);
else return false;
}
void cPict::setFormat(eFormat prop, short val) throw(xUnsupportedProp){
if(prop == TXT_FRAME) drawFramed = val;
else if(prop == TXT_FRAMESTYLE) frameStyle = eFrameStyle(val);
@@ -148,16 +129,22 @@ ePicType cPict::getPicType(){
cPict::cPict(cDialog& parent) :
cControl(CTRL_PICT,parent),
drawFramed(true),
clickable(false) {}
drawFramed(true) {}
cPict::cPict(sf::RenderWindow& parent) :
cControl(CTRL_PICT, parent),
drawFramed(true),
clickable(false) {}
drawFramed(true) {}
bool cPict::isClickable(){
return clickable;
return haveHandler(EVT_CLICK);
}
bool cPict::isFocusable(){
return false;
}
bool cPict::isScrollable(){
return false;
}
ePicType operator+ (ePicType lhs, ePicTypeMod rhs){

View File

@@ -26,9 +26,6 @@ public:
/// @copydoc cDialog::init()
static void init();
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw();
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp);
@@ -66,6 +63,8 @@ public:
/// @param parent The parent window.
explicit cPict(sf::RenderWindow& parent);
bool isClickable();
bool isFocusable();
bool isScrollable();
/// Advance the current animation frame.
/// Should be called at predictable intervals if the dialog might contain an animated graphic.
static void advanceAnim();
@@ -81,6 +80,12 @@ public:
/// A convenience constant that can be passed as the pic number to setPict(pic_num_t num).
/// It sets the icon to nothing, showing as just black.
static const pic_num_t BLANK;
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK};
}
cPict& operator=(cPict& other) = delete;
cPict(cPict& other) = delete;
private:
@@ -88,7 +93,7 @@ private:
static short animFrame;
pic_num_t picNum;
ePicType picType;
bool clickable, drawFramed, drawScaled;
bool drawFramed, drawScaled;
void drawPresetTer(short num, rectangle to_rect);
void drawPresetTerAnim(short num, rectangle to_rect);
void drawPresetMonstSm(short num, rectangle to_rect);
@@ -131,7 +136,6 @@ private:
void drawPartyItem(short num, rectangle to_rect);
void drawPartyPc(short num, rectangle to_rect);
static std::map<ePicType,void(cPict::*)(short,rectangle)>& drawPict();
click_callback_t onClick;
};
#endif

View File

@@ -26,6 +26,14 @@ bool cScrollbar::isClickable(){
return true;
}
bool cScrollbar::isFocusable(){
return false;
}
bool cScrollbar::isScrollable(){
return true;
}
void cScrollbar::setPosition(long newPos) {
pos = minmax(0,max,newPos);
}
@@ -75,20 +83,6 @@ eScrollStyle cScrollbar::getStyle() {
return style;
}
void cScrollbar::attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) {
onClick = f;
}
void cScrollbar::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported) {
throw xHandlerNotSupported(true);
}
bool cScrollbar::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods) {
// TODO: Implement detection of scrolling stuff, maybe even dragging the thumb
if(onClick != nullptr) return onClick(me,id,mods);
return false;
}
bool cScrollbar::handleClick(location where) {
if(max == 0) return false;
sf::Event e;

View File

@@ -44,7 +44,6 @@ class cScrollbar : public cControl {
} pressedPart;
eScrollStyle style = SCROLL_WHITE;
bool vert = true;
click_callback_t onClick;
static sf::Texture scroll_gw[NUM_STYLES];
static const rectangle up_rect[NUM_STYLES][4], down_rect[NUM_STYLES][4], bar_rect[NUM_STYLES][4], thumb_rect[NUM_STYLES][4];
void draw_vertical(), draw_horizontal();
@@ -58,9 +57,6 @@ public:
/// Create a new scrollbar.
/// @param parent The parent dialog.
explicit cScrollbar(cDialog& parent);
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool handleClick(location where);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
@@ -69,6 +65,8 @@ public:
storage_t store();
void restore(storage_t to);
bool isClickable();
bool isFocusable();
bool isScrollable();
/// Get the scrollbar thumb's current position.
/// @return The current position.
long getPosition();
@@ -118,6 +116,12 @@ public:
/// @param newStyle The new style.
void setStyle(eScrollStyle newStyle);
void draw();
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK, EVT_SCROLL};
}
cScrollbar& operator=(cScrollbar& other) = delete;
cScrollbar(cScrollbar& other) = delete;
};

View File

@@ -17,27 +17,25 @@ cScrollPane::cScrollPane(cDialog& parent) : cContainer(CTRL_PANE, parent), scrol
recalcRect();
}
void cScrollPane::attachClickHandler(click_callback_t) throw(xHandlerNotSupported) {
throw xHandlerNotSupported(false);
}
void cScrollPane::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported) {
throw xHandlerNotSupported(true);
}
bool cScrollPane::triggerClickHandler(cDialog&, std::string, eKeyMod) {
return true;
}
bool cScrollPane::handleClick(location where) {
if(scroll.getBounds().contains(where))
return scroll.handleClick(where);
where.y += scroll.getPosition();
for(auto& ctrl : contents) {
if(ctrl.second->isClickable() && ctrl.second->getBounds().contains(where))
return ctrl.second->handleClick(where);
}
return false;
return cContainer::handleClick(where);
}
bool cContainer::handleClick(location where) {
std::string which_clicked;
bool success = false;
forEach([&](std::string id, cControl& ctrl) {
if(ctrl.isClickable() && ctrl.getBounds().contains(where)) {
success = ctrl.handleClick(where);
which_clicked = id;
}
});
if(!which_clicked.empty())
clicking = which_clicked;
return success;
}
void cScrollPane::recalcRect() {
@@ -114,6 +112,14 @@ bool cScrollPane::isClickable() {
return true;
}
bool cScrollPane::isFocusable() {
return false;
}
bool cScrollPane::isScrollable() {
return true;
}
long cScrollPane::getPosition() {
return scroll.getPosition();
}
@@ -166,6 +172,11 @@ void cScrollPane::draw() {
drawFrame(4, frameStyle);
}
void cScrollPane::forEach(std::function<void(std::string,cControl&)> callback) {
for(auto ctrl : contents)
callback(ctrl.first, *ctrl.second);
}
std::string cScrollPane::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
Iterator<Attribute> attr;

View File

@@ -24,9 +24,6 @@ public:
/// Create a new scroll pane
explicit cScrollPane(cDialog& parent);
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool handleClick(location where);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
@@ -37,6 +34,8 @@ public:
storage_t store();
void restore(storage_t to);
bool isClickable();
bool isFocusable();
bool isScrollable();
/// Add a new control to this pane.
/// @param ctrl A pointer to the control, which should already be constructed.
/// @param key A key to be used to look up the control later.
@@ -60,6 +59,13 @@ public:
/// @param newStyle The new style.
void setStyle(eScrollStyle newStyle);
void draw();
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK, EVT_SCROLL, EVT_FOCUS, EVT_DEFOCUS};
}
void forEach(std::function<void(std::string,cControl&)> callback) override;
cScrollPane& operator=(cScrollPane& other) = delete;
cScrollPane(cScrollPane& other) = delete;
};

View File

@@ -22,39 +22,13 @@ cControl& cStack::getChild(std::string id) {
return *controls[id];
}
void cStack::attachClickHandler(click_callback_t f) throw(xHandlerNotSupported) {
onClick = f;
}
void cStack::attachFocusHandler(focus_callback_t) throw(xHandlerNotSupported) {
throw xHandlerNotSupported(true);
}
bool cStack::triggerClickHandler(cDialog& me, std::string id, eKeyMod mods) {
void cContainer::callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, std::string id, eKeyMod mods) {
std::string which_clicked = clicking;
clicking = "";
if(onClick) onClick(me, id, mods);
return controls[which_clicked]->triggerClickHandler(me, which_clicked, mods);
}
bool cStack::handleClick(location where) {
std::string which_clicked;
auto iter = controls.begin();
while(iter != controls.end()){
if(iter->second->isVisible() && where.in(iter->second->getBounds())){
if(iter->second->handleClick(where)) {
which_clicked = iter->first;
break;
}
}
iter++;
}
if(which_clicked == "") return false;
clicking = which_clicked;
return true;
if(!which_clicked.empty())
getChild(which_clicked).triggerClickHandler(me, which_clicked, mods);
}
void cStack::setFormat(eFormat prop, short val) throw(xUnsupportedProp) {
@@ -82,6 +56,14 @@ bool cStack::isClickable() {
return true;
}
bool cStack::isFocusable() {
return false;
}
bool cStack::isScrollable() {
return false;
}
void cStack::draw() {
if(!isVisible()) return;
for(auto& p : controls) {
@@ -167,6 +149,11 @@ void cStack::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reve
cStack::cStack(cDialog& parent) : cContainer(CTRL_STACK, parent), curPage(0), nPages(0) {}
void cStack::forEach(std::function<void(std::string,cControl&)> callback) {
for(auto ctrl : controls)
callback(ctrl.first, *ctrl.second);
}
std::string cStack::parse(ticpp::Element& who, std::string fname) {
using namespace ticpp;
Iterator<Attribute> attr;

View File

@@ -36,19 +36,16 @@ class cStack : public cContainer {
std::string clicking;
std::vector<std::map<std::string,storage_t>> storage;
std::map<std::string,cControl*> controls;
click_callback_t onClick;
bool drawFramed;
bool drawFramed = false;
public:
std::string parse(ticpp::Element& who, std::string fname);
void attachClickHandler(click_callback_t f) throw(xHandlerNotSupported);
void attachFocusHandler(focus_callback_t f) throw(xHandlerNotSupported);
bool triggerClickHandler(cDialog& me, std::string id, eKeyMod mods);
bool handleClick(location where);
void setFormat(eFormat prop, short val) throw(xUnsupportedProp);
short getFormat(eFormat prop) throw(xUnsupportedProp);
void setColour(sf::Color clr) throw(xUnsupportedProp);
sf::Color getColour() throw(xUnsupportedProp);
bool isClickable();
bool isFocusable();
bool isScrollable();
void draw();
bool hasChild(std::string id);
cControl& getChild(std::string id);
@@ -79,6 +76,13 @@ public:
/// Create a new stack
/// @param parent The parent dialog.
cStack(cDialog& parent);
/// @copydoc cControl::getSupportedHandlers
///
/// @todo Document possible handlers
const std::set<eDlogEvt> getSupportedHandlers() const {
return {EVT_CLICK, EVT_FOCUS, EVT_DEFOCUS};
}
void forEach(std::function<void(std::string,cControl&)> callback) override;
cStack& operator=(cStack& other) = delete;
cStack(cStack& other) = delete;
};

View File

@@ -30,7 +30,7 @@ are required. Some controls may ignore the `width` and `height`
attributes.
* `def-key` - Specifies the default keyboard shortcut for the
control. See **Keyboard Shortcuts** below for more information on the
format of these attributes.
format of this attribute.
* `font`, `size`, `color`, `colour` - Specifies text attributes of the
control. See **Text Formatting** below for details on accepted values.
* `name` - Give the control a unique identifier which you can use to
@@ -72,9 +72,6 @@ The `<text>` tag accepts the following attributes:
* `framed` - See **Common Attributes** above. Defaults to `false`.
* `outline` - See **Common Attributes** above.
* `clickable` - Specifies that the text is clickable. This attribute is
useless, as clickability is determined solely by having a click handler
set in the code.
* `fromlist`, `font`, `size`, `color`, `colour`, `def-key` -
See **Common Attributes** above.
@@ -106,19 +103,20 @@ The possible values for the `type` attribute are:
* `right` - A normal-sized 63x23 button with a right-pointing arrow.
* `up` - A normal-sized 63x23 button with an up-pointing arrow.
* `down` - A normal-sized 63x23 button with a down-pointing arrow.
* `tiny` - A tiny 14x10 button, same size as an LED.
* `tiny` - A tiny 14x10 button, same size as an LED and with the same
label behaviour.
* `done` - A normal-sized 63x23 button with "Done" on it.
* `tall` - A tall 63x40 button.
* `trait` - A tall 63x40 button with "Race Good/Bad Traits" on it.
* `push` - A round red 30x30 push button.
(Naturally, they correspond perfectly with the values of the eBtnType
(Naturally, they correspond perfectly with the values of the @ref eBtnType
enumeration.)
The `<led>` tag
---------------
The `<led>` tag descripts an LED button. It can contain label text,
The `<led>` tag describes an LED button. It can contain label text,
which is drawn to the right of the button graphic. This differs from
normal practice in the original Blades of Exile dialog engine, where LED
labels were typically left-aligned to the _left_ of their associated
@@ -158,9 +156,6 @@ The `<pict>` tag accepts the following attributes:
* `type` - Specifies the type of graphic. This attribute is required.
* `num` - Specifies the graphic number. This attribute is required.
* `clickable` - Specifies that the picture is clickable. This attribute
is useless, as clickability is determined solely by having a click
handler set in the code.
* `custom` - Specifies whether the picture is custom or preset. Can be
either `true` or `false`; defaults to `false`.
* `framed` - See **Common Attributes** above. Defaults to `true`.

View File

@@ -23,7 +23,7 @@
/// Resources include sounds, images, fonts, and cursors.
///
/// To implement a new resource type, all you have to do is specialize
/// @a ResMgr::resLoader::operator() and declare @a ResMgr::resLoader::file_ext
/// @ref ResMgr::resLoader::operator()() and declare @ref ResMgr::resLoader::file_ext
/// for the desired resource type. The operator() receives the
/// full file path with the extension already applied.
namespace ResMgr {