Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Record scrollbar movement in dialogs #662

Merged
merged 5 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/dialogxml/dialogs/dialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ void cDialog::loadFromFile(const DialogDefn& file){
}
}
prevCtrl = *inserted;
prevCtrl.second->setName(prevCtrl.first);
// Needed to correctly resolve relative positioning
inserted->second->recalcRect();
}
Expand Down Expand Up @@ -464,6 +465,7 @@ bool cDialog::add(cControl* what, rectangle ctrl_frame, std::string key){
if(controls.find(key) != controls.end()) return false;
what->setBounds(ctrl_frame);
controls.insert(std::make_pair(key,what));
what->setName(key);
return true;
}

Expand Down Expand Up @@ -567,6 +569,16 @@ void cDialog::handle_events() {
}else if(replaying && has_next_action("field_selection")) {
cTextField& text_field = dynamic_cast<cTextField&>(getControl(currentFocus));
text_field.replay_selection(pop_next_action());
}else if(replaying && has_next_action("scrollbar_setPosition")){
Element& next_action = pop_next_action();
auto info = info_from_action(next_action);
std::string name = info["name"];
long newPos = boost::lexical_cast<long>(info["newPos"]);

cScrollPane& pane = dynamic_cast<cScrollPane&>(getControl(name));
pane.getScroll().setPosition(newPos);
}else if(replaying && has_next_action()){
throw std::string { "Replaying a dialog, have the wrong replay action: " + next_action_type() };
}else{
while(pollEvent(win, currentEvent)){
handle_one_event(currentEvent, fps_limiter);
Expand Down
28 changes: 20 additions & 8 deletions src/dialogxml/widgets/container.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,50 @@
#include "dialogxml/widgets/scrollbar.hpp"
#include "replay.hpp"

bool cContainer::parseChildControl(ticpp::Element& elem, std::map<std::string,cControl*>& controls, std::string& id) {
bool cContainer::parseChildControl(ticpp::Element& elem, std::map<std::string,cControl*>& controls, std::string& id, std::string fname) {
ctrlIter inserted;
std::string tag = elem.Value();
if(tag == "field") {
auto field = parent->parse<cTextField>(elem);
controls.insert(field);
inserted = controls.insert(field).first;
parent->tabOrder.push_back(field);
id = field.first;
} else if(tag == "text") {
auto text = parent->parse<cTextMsg>(elem);
controls.insert(text);
inserted = controls.insert(text).first;
id = text.first;
} else if(tag == "pict") {
auto pict = parent->parse<cPict>(elem);
controls.insert(pict);
inserted = controls.insert(pict).first;
id = pict.first;
} else if(tag == "slider") {
auto slide = parent->parse<cScrollbar>(elem);
controls.insert(slide);
inserted = controls.insert(slide).first;
id = slide.first;
} else if(tag == "button") {
auto button = parent->parse<cButton>(elem);
controls.insert(button);
inserted = controls.insert(button).first;
id = button.first;
} else if(tag == "led") {
auto led = parent->parse<cLed>(elem);
controls.insert(led);
inserted = controls.insert(led).first;
id = led.first;
} else if(tag == "group") {
auto group = parent->parse<cLedGroup>(elem);
controls.insert(group);
inserted = controls.insert(group).first;
id = group.first;
} else return false;
if(prevCtrl.second) {
if(inserted->second->anchor == "$$prev$$" && prevCtrl.second->anchor == "$$next$$") {
throw xBadVal(tag, "anchor", "<circular dependency>", elem.Row(), elem.Column(), fname);
} else if(inserted->second->anchor == "$$prev$$") {
inserted->second->anchor = prevCtrl.first;
} else if(prevCtrl.second->anchor == "$$next$$") {
prevCtrl.second->anchor = inserted->first;
}
}
prevCtrl = *inserted;
prevCtrl.second->setName(prevCtrl.first);
return true;
}

Expand Down
6 changes: 5 additions & 1 deletion src/dialogxml/widgets/container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include "control.hpp"

typedef std::map<std::string,cControl*>::iterator ctrlIter;

/// 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;
Expand All @@ -21,7 +23,7 @@ class cContainer : public cControl {
/// @param controls The map into which the control will be inserted.
/// @param[out] The ID of the new control.
/// @return true if the element was a valid control, false otherwise.
bool parseChildControl(ticpp::Element& elem, std::map<std::string,cControl*>& controls, std::string& id);
bool parseChildControl(ticpp::Element& elem, std::map<std::string,cControl*>& controls, std::string& id, std::string fname);
public:
/// Create a new container control attached to an arbitrary window, rather than a dialog.
/// @param t The type of the control.
Expand Down Expand Up @@ -49,6 +51,8 @@ class cContainer : public cControl {
const cControl& operator[](std::string id) const {return const_cast<cContainer&>(*this).getChild(id);}
bool isContainer() const override {return true;}
bool handleClick(location where, cFramerateLimiter& fps_limiter) override;
private:
std::pair<std::string,cControl*> prevCtrl{"", nullptr};
};

#endif
6 changes: 5 additions & 1 deletion src/dialogxml/widgets/control.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ class cControl {
/// @throw xMissingAttr if a required attribute is missing
/// @throw xMissingElem if a required attribute is either missing or present in insufficient quantity
virtual void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes);
std::string name;
public:
/// Attach a keyboard shortcut to a control. Pressing the keyboard shortcut is equivalent to clicking the control.
/// @param key The desired keyboard shortcut.
Expand Down Expand Up @@ -181,6 +182,8 @@ class cControl {
if(old_handler.empty()) return nullptr;
return boost::any_cast<typename event_fcn<t>::type>(old_handler);
}
inline std::string getName() { return name; }
inline void setName(std::string value) { name = value; }
/// 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.
Expand Down Expand Up @@ -459,7 +462,8 @@ class cControl {
/// Plays the proper sound for this control being clicked on
void playClickSound();
private:
friend class cDialog; // TODO: This is only so it can access parseColour... hack!
friend class cDialog; // This is so it can access parseColour and anchor
friend class cContainer; // This is so it can access anchor
/// The control's current text.
std::string lbl;
eControlType type;
Expand Down
7 changes: 3 additions & 4 deletions src/dialogxml/widgets/scrollbar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,13 @@ bool cScrollbar::isScrollable() const {
return true;
}

void cScrollbar::setName(std::string name) {
this->name = name;
}

void cScrollbar::setPosition(long newPos, bool record) {
if(record && recording){
std::map<std::string,std::string> info;
info["name"] = name;
if(info["name"].empty() && pane != nullptr){
info["name"] = pane->getName();
}
// Might as well record newPos before it gets clamped, so replays will verify that clamping
// still works.
info["newPos"] = boost::lexical_cast<std::string>(newPos);
Expand Down
4 changes: 2 additions & 2 deletions src/dialogxml/widgets/scrollbar.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ enum eScrollStyle {
/// using the methods to get the scrollbar's position.
/// Alternatively, it can be used as a slider control.
class cScrollbar : public cControl, public iEventListener, public iDrawable {
std::string name;
cControl* pane;
int pos, max, pgsz;
std::string link;
// Make sure this is equal to the number of constants in eScrollStyle
Expand Down Expand Up @@ -66,6 +66,7 @@ class cScrollbar : public cControl, public iEventListener, public iDrawable {
public:
/// @copydoc cDialog::init()
static void init();
inline void setPane(cControl* pane) { this->pane = pane; }
bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
location getPreferredSize() const override;
Expand Down Expand Up @@ -105,7 +106,6 @@ class cScrollbar : public cControl, public iEventListener, public iDrawable {
/// Get the scrollbar style.
/// @return The style
eScrollStyle getStyle() const;
void setName(std::string name);
/// Set the scrollbar thumb's current position.
/// @param to The new position.
void setPosition(long to, bool record = false);
Expand Down
3 changes: 2 additions & 1 deletion src/dialogxml/widgets/scrollpane.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <climits>

cScrollPane::cScrollPane(cDialog& parent) : cContainer(CTRL_PANE, parent), scroll(parent) {
scroll.setPane(this);
recalcRect();
}

Expand Down Expand Up @@ -173,7 +174,7 @@ bool cScrollPane::parseAttribute(ticpp::Attribute& attr, std::string tagName, st
bool cScrollPane::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
if(content.Type() == TiXmlNode::ELEMENT) {
std::string id;
return parseChildControl(dynamic_cast<ticpp::Element&>(content), contents, id);
return parseChildControl(dynamic_cast<ticpp::Element&>(content), contents, id, fname);
}
return cContainer::parseContent(content, n, tagName, fname, text);
}
Expand Down
1 change: 1 addition & 0 deletions src/dialogxml/widgets/scrollpane.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class cScrollPane : public cContainer {
public:
/// Create a new scroll pane
explicit cScrollPane(cDialog& parent);
inline cScrollbar& getScroll() { return scroll; }
bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
Expand Down
4 changes: 2 additions & 2 deletions src/dialogxml/widgets/stack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ bool cStack::parseContent(ticpp::Node& content, int n, std::string tagName, std:
Iterator<Element> iter;
for(iter = iter.begin(&elem); iter != iter.end(); iter++) {
if(iter->Type() == TiXmlNode::ELEMENT) {
if(!parseChildControl(*iter, controls, id)) return false;
if(!parseChildControl(*iter, controls, id, fname)) return false;
templates.back().push_back(id);
}
}
Expand Down Expand Up @@ -206,7 +206,7 @@ bool cStack::parseContent(ticpp::Node& content, int n, std::string tagName, std:
} else throw xBadAttr("page", name, attr->Row(), attr->Column(), fname);
}
return true;
} else return parseChildControl(elem, controls, id);
} else return parseChildControl(elem, controls, id, fname);
}
return cContainer::parseContent(content, n, tagName, fname, text);
}
Expand Down
Loading