Add an SDF picker for selecting a stuff done flag and optionally giving it a name.
In effect, this is a combination of two of the previous pickers: the location picker, and the editable string picker. This required quite a significant rework of how the tilemap places its children. Currently it's only used in special node editing. I plan to add its use in many other places too though.
This commit is contained in:
@@ -110,6 +110,7 @@
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.locpicker.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.main.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.menus.win.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.sdfpicker.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.townout.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -122,6 +123,7 @@
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.keydlgs.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.locpicker.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.menus.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.sdfpicker.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.townout.hpp" />
|
||||
<ClInclude Include="..\..\..\rsrc\menus\scenresource.h" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.townout.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.sdfpicker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.menus.win.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -53,6 +56,9 @@
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.townout.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.sdfpicker.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.menus.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.locpicker.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.main.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.menus.win.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.sdfpicker.cpp" />
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.townout.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\fileio_party.cpp" />
|
||||
<ClCompile Include="..\..\..\src\universe\universe.cpp" />
|
||||
@@ -179,6 +180,7 @@
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.graphics.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.keydlgs.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.locpicker.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.sdfpicker.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.menus.hpp" />
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.townout.hpp" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.menus.win.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.sdfpicker.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\scenedit\scen.townout.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -83,6 +87,9 @@
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.menus.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.sdfpicker.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\scenedit\scen.townout.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
915AF9E81BBF8B5C008AEF49 /* scrollpane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 919B13A81BBE2B54009905A4 /* scrollpane.cpp */; };
|
||||
9170C50F2D717F24009B6E7C /* scen.locpicker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9170C50C2D717F24009B6E7C /* scen.locpicker.cpp */; };
|
||||
9170C5102D717F24009B6E7C /* scen.locpicker.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 9170C50D2D717F24009B6E7C /* scen.locpicker.hpp */; };
|
||||
9170C5272D74237A009B6E7C /* scen.sdfpicker.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9170C5252D74237A009B6E7C /* scen.sdfpicker.cpp */; };
|
||||
9176FEC71D550EFE006EF694 /* out_legacy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9176FEC01D550EFC006EF694 /* out_legacy.cpp */; };
|
||||
9176FEC81D550EFE006EF694 /* scen_legacy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9176FEC11D550EFC006EF694 /* scen_legacy.cpp */; };
|
||||
9176FECB1D550EFE006EF694 /* talk_legacy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9176FEC41D550EFD006EF694 /* talk_legacy.cpp */; };
|
||||
@@ -738,6 +739,8 @@
|
||||
9169C31F1B37A5D50041002B /* BoE Scenario Editor.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "BoE Scenario Editor.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
9170C50C2D717F24009B6E7C /* scen.locpicker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = scen.locpicker.cpp; sourceTree = "<group>"; };
|
||||
9170C50D2D717F24009B6E7C /* scen.locpicker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = scen.locpicker.hpp; sourceTree = "<group>"; };
|
||||
9170C5252D74237A009B6E7C /* scen.sdfpicker.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = scen.sdfpicker.cpp; sourceTree = "<group>"; };
|
||||
9170C5262D74237A009B6E7C /* scen.sdfpicker.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = scen.sdfpicker.hpp; sourceTree = "<group>"; };
|
||||
9176FEC01D550EFC006EF694 /* out_legacy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = out_legacy.cpp; sourceTree = "<group>"; };
|
||||
9176FEC11D550EFC006EF694 /* scen_legacy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = scen_legacy.cpp; sourceTree = "<group>"; };
|
||||
9176FEC41D550EFD006EF694 /* talk_legacy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = talk_legacy.cpp; sourceTree = "<group>"; };
|
||||
@@ -1452,6 +1455,7 @@
|
||||
91B3EEE00F969BA700BF5B67 /* scen.keydlgs.hpp */,
|
||||
9170C50D2D717F24009B6E7C /* scen.locpicker.hpp */,
|
||||
914CA4641909B00100B6ADD1 /* scen.menus.hpp */,
|
||||
9170C5262D74237A009B6E7C /* scen.sdfpicker.hpp */,
|
||||
91B3EEE60F969BA700BF5B67 /* scen.townout.hpp */,
|
||||
);
|
||||
name = headers;
|
||||
@@ -1470,6 +1474,7 @@
|
||||
91B3EEEB0F969BA700BF5B67 /* scen.main.cpp */,
|
||||
9170C50C2D717F24009B6E7C /* scen.locpicker.cpp */,
|
||||
914CA45719074D0A00B6ADD1 /* scen.menus.mac.mm */,
|
||||
9170C5252D74237A009B6E7C /* scen.sdfpicker.cpp */,
|
||||
91B3EEF40F969BA700BF5B67 /* scen.townout.cpp */,
|
||||
);
|
||||
name = src;
|
||||
@@ -2206,6 +2211,7 @@
|
||||
413AAF652D38A48E002E9BF1 /* pc.cpp in Sources */,
|
||||
413AAF642D38A47C002E9BF1 /* population.cpp in Sources */,
|
||||
9170C50F2D717F24009B6E7C /* scen.locpicker.cpp in Sources */,
|
||||
9170C5272D74237A009B6E7C /* scen.sdfpicker.cpp in Sources */,
|
||||
413AAF632D38A1B8002E9BF1 /* party.cpp in Sources */,
|
||||
413AAF622D38A076002E9BF1 /* universe.cpp in Sources */,
|
||||
413AAF612D389F94002E9BF1 /* fileio_party.cpp in Sources */,
|
||||
|
||||
25
rsrc/dialogs/choose-sdf.xml
Normal file
25
rsrc/dialogs/choose-sdf.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
|
||||
<dialog defbtn='done'>
|
||||
<pict name='mainpic' type='dlog' num='16' top='6' left='6'/>
|
||||
<text name='prompt' anchor='mainpic' relative='pos pos-in' size='large' top='0' left='12' width='248' height='14'>Select a Stuff Done Flag:</text>
|
||||
<tilemap name='cols' anchor='mainpic' relative='pos' top='8' left='0' rows='1' cols='5' cellspacing='10'>
|
||||
<text name='col' size='large' left='0' top='0' width='120' height='14' framed='false'>A</text>
|
||||
</tilemap>
|
||||
<tilemap name='rows' anchor='mainpic' relative='pos-in pos' top='24' left='0' rows='10' cols='1' cellspacing='10'>
|
||||
<text name='row' size='large' left='0' top='0' width='35' height='14' framed='false'>1</text>
|
||||
</tilemap>
|
||||
<tilemap name='map' anchor='mainpic' relative='pos' framed='true' top='24' left='0' rows='10' cols='5' cellspacing='10'>
|
||||
<mapgroup name='choice'>
|
||||
<led name='select' left='0' top='2'/>
|
||||
</mapgroup>
|
||||
<field name='name' anchor='select' relative='pos neg' left='6' top='2' height='14' width='100'/>
|
||||
</tilemap>
|
||||
<text name='help' anchor='left' relative='pos-in pos' top='0' left='6' width='422' height='16'>Click button to left of graphic to select.<br/>Use arrows to change pages.</text>
|
||||
<button name='left' anchor='map' relative='pos-in pos' type='left' top='10' left='0' def-key='left'/>
|
||||
<button name='down' anchor='left' relative='pos pos-in' type='down' top='0' left='228' def-key='down'/>
|
||||
<button name='right' anchor='down' relative='pos pos-in' type='right' top='0' left='228' def-key='right'/>
|
||||
<button name='cancel' anchor='done' relative='neg pos-in' type='regular' top='0' left='67' def-key='esc'>Cancel</button>
|
||||
<button name='done' anchor='right' relative='pos-in pos' type='done' top='4' left='0'/>
|
||||
<button name='up' anchor='down' relative='pos-in neg' type='up' top='284' left='0' def-key='up'/>
|
||||
</dialog>
|
||||
@@ -307,6 +307,7 @@
|
||||
<xs:element name="button" type="button"/>
|
||||
<xs:element name="led" type="led"/>
|
||||
<xs:element name="group" type="ledGroup"/>
|
||||
<xs:element name="mapgroup" type="ledGroup"/>
|
||||
<xs:element name="slider" type="slider"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:token"/>
|
||||
|
||||
@@ -314,6 +314,7 @@ attribute specifies which sound it applies to.
|
||||
* `<event>` - (max unbounded) Gives a name to a major event flag. These names are shown (and editable) when showing the Pick Event dialog in various places. The required `id` attribute specifies which event.
|
||||
* `<item-class>` - (max unbounded) Gives a name to an item special class. These names are shown (and editable) when showing the Pick Item Class dialog in various places. The required `id` attribute specifies which class.
|
||||
* `<item-typeflag>` - (max unbounded) Gives a name to an item type flag. These names are shown (and editable) when showing the Pick Item Type Flag dialog in the item editor. The required `id` attribute specifies which flag.
|
||||
* `<sdf>` - (max unbounded) Gives a name to a Stuff Done Flag. These names are shown (and editable) when showing the Pick Stuff Done Flag dialog in the item editor. The required `row` and `col` attributes specify which flag.
|
||||
|
||||
Terrain Types
|
||||
-------------
|
||||
|
||||
@@ -391,6 +391,28 @@
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
<xs:element name="sdf" minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:complexType>
|
||||
<xs:simpleContent>
|
||||
<xs:extension base="xs:string">
|
||||
<xs:attribute name="row" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:maxExclusive value="350"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attribute name="row" use="required">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:integer">
|
||||
<xs:maxExclusive value="50"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
</xs:extension>
|
||||
</xs:simpleContent>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
<xs:element name="scenario">
|
||||
|
||||
@@ -307,13 +307,21 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
||||
}
|
||||
});
|
||||
if(!all_children_resolved) return;
|
||||
pane->recalcRect();
|
||||
pane->postChildrenResolve();
|
||||
}
|
||||
ctrl.relocateRelative(ctrl.frame.topLeft(), anchor, ctrl.horz, ctrl.vert);
|
||||
ctrl.anchor.clear();
|
||||
ctrl.horz = ctrl.vert = POS_ABS;
|
||||
} else if(auto pane = dynamic_cast<cContainer*>(&ctrl)) {
|
||||
pane->forEach(process_ctrl);
|
||||
bool all_children_resolved = true;
|
||||
pane->forEach([&process_ctrl, &all_children_resolved](std::string id, cControl& ctrl) {
|
||||
if(!ctrl.anchor.empty()) {
|
||||
all_children_resolved = false;
|
||||
}
|
||||
process_ctrl(id, ctrl);
|
||||
});
|
||||
if(!all_children_resolved) return;
|
||||
pane->postChildrenResolve();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -96,3 +96,13 @@ void cContainer::callHandler(event_fcn<EVT_CLICK>::type onClick, cDialog& me, st
|
||||
if(!which_clicked.empty())
|
||||
getChild(which_clicked).triggerClickHandler(me, which_clicked, mods);
|
||||
}
|
||||
|
||||
void cContainer::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
|
||||
cControl::validatePostParse(who, fname, attrs, nodes);
|
||||
if(!anchor.empty()) return;
|
||||
bool allResolved = true;
|
||||
forEach([&allResolved](std::string, cControl& ctrl) {
|
||||
if(!ctrl.anchor.empty()) allResolved = false;
|
||||
});
|
||||
if(allResolved) postChildrenResolve();
|
||||
}
|
||||
|
||||
@@ -46,11 +46,15 @@ public:
|
||||
/// This may be called more than once, so it should not return the same value twice in a row,
|
||||
/// unless it can guarantee the value is not already assigned to another control.
|
||||
virtual std::string generateId(const std::string& explicitId) const override;
|
||||
virtual void postChildrenResolve() {
|
||||
recalcRect();
|
||||
}
|
||||
/// @copydoc getChild()
|
||||
cControl& operator[](std::string id) {return getChild(id);}
|
||||
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;
|
||||
void validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) override;
|
||||
private:
|
||||
std::pair<std::string,cControl*> prevCtrl{"", nullptr};
|
||||
};
|
||||
|
||||
@@ -360,6 +360,19 @@ public:
|
||||
virtual void draw() = 0;
|
||||
cControl& operator=(cControl& other) = delete;
|
||||
cControl(cControl& other) = delete;
|
||||
/// Sets the positioning method of this control.
|
||||
/// Note: this only has an effect when called from another control's parse function.
|
||||
void setPositioning(const std::string& anchor_id, ePosition h, ePosition v) {
|
||||
anchor = anchor_id;
|
||||
horz = h;
|
||||
vert = v;
|
||||
}
|
||||
/// Get the positioning method of this control.
|
||||
/// Note: After the parse stage, this information is lost and replaced with (ABS,ABS).
|
||||
std::pair<ePosition, ePosition> getPositioning(std::string* anchor_id = nullptr) {
|
||||
if(anchor_id) *anchor_id = anchor;
|
||||
return {horz, vert};
|
||||
}
|
||||
protected:
|
||||
/// Create a new control attached to a dialog.
|
||||
/// @param t The type of the control.
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
/// 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 {
|
||||
friend class cTilemap; // So it can call parseAttribute
|
||||
std::map<std::string,cLed*> choices;
|
||||
std::string fromList;
|
||||
std::string curSelect, prevSelect;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "button.hpp"
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
#include "dialogxml/widgets/ledgroup.hpp"
|
||||
#include "field.hpp"
|
||||
#include "message.hpp"
|
||||
#include "pict.hpp"
|
||||
@@ -24,12 +25,21 @@ std::string cTilemap::generateId(const std::string& baseId) const {
|
||||
return baseId + "-" + current_cell;
|
||||
}
|
||||
|
||||
location cTilemap::getCell(const std::string& id) const {
|
||||
location cTilemap::getCellIdx(const std::string& id) const {
|
||||
size_t y_pos = id.find_last_of("y");
|
||||
size_t x_pos = id.find_last_of("x");
|
||||
if(y_pos == std::string::npos && x_pos == std::string::npos)
|
||||
return location();
|
||||
std::string x_str = id.substr(x_pos + 1, y_pos);
|
||||
std::string y_str = id.substr(y_pos + 1);
|
||||
return location(std::stoi(x_str) * cellWidth, std::stoi(y_str) * cellHeight);
|
||||
return location(std::stoi(x_str), std::stoi(y_str));
|
||||
}
|
||||
|
||||
location cTilemap::getCell(const std::string &id) const {
|
||||
location idx = getCellIdx(id);
|
||||
idx.x *= cellWidth;
|
||||
idx.y *= cellHeight;
|
||||
return idx;
|
||||
}
|
||||
|
||||
location cTilemap::getCellPos(size_t x, size_t y) const {
|
||||
@@ -39,11 +49,17 @@ location cTilemap::getCellPos(size_t x, size_t y) const {
|
||||
}
|
||||
|
||||
location cTilemap::getCellPos(cControl& child) const {
|
||||
// If it's not ours, return origin.
|
||||
if(!hasChild(child.getName())) return location();
|
||||
// TODO: If it's not ours, return origin. (Not done right now because hasChild fails for children of children.)
|
||||
// if(!hasChild(child.getName())) return location();
|
||||
return getCell(child.getName());
|
||||
}
|
||||
|
||||
location cTilemap::getCellIdx(cControl &child) const {
|
||||
// TODO: If it's not ours, return origin. (Not done right now because hasChild fails for children of children.)
|
||||
// if(!hasChild(child.getName())) return location();
|
||||
return getCellIdx(child.getName());
|
||||
}
|
||||
|
||||
std::string cTilemap::buildId(const std::string& base, size_t x, size_t y) {
|
||||
std::ostringstream sout;
|
||||
if(!base.empty()) {
|
||||
@@ -104,43 +120,44 @@ bool cTilemap::isScrollable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cTilemap::handleClick(location where, cFramerateLimiter& fps_limiter) {
|
||||
bool success = false;
|
||||
isHandlingClick = true;
|
||||
forEach([&](std::string id, cControl& ctrl) {
|
||||
rectangle bounds = ctrl.getBounds();
|
||||
bounds.offset(getBounds().topLeft());
|
||||
bounds.offset(getCell(id));
|
||||
if(!success && ctrl.isClickable() && bounds.contains(where)) {
|
||||
rectangle localBounds = ctrl.getBounds();
|
||||
ctrl.setBounds(bounds);
|
||||
clicking = id;
|
||||
if(ctrl.handleClick(where, fps_limiter)){
|
||||
success = true;
|
||||
} else clicking.clear();
|
||||
ctrl.setBounds(localBounds);
|
||||
}
|
||||
});
|
||||
isHandlingClick = false;
|
||||
return success;
|
||||
}
|
||||
|
||||
void cTilemap::draw() {
|
||||
if(!isVisible()) return;
|
||||
for(auto& ctrl : controls) {
|
||||
rectangle localBounds = ctrl.second->getBounds();
|
||||
rectangle globalBounds = localBounds;
|
||||
if(isHandlingClick ? ctrl.first != clicking : true) {
|
||||
globalBounds.offset(getBounds().topLeft());
|
||||
globalBounds.offset(getCell(ctrl.first));
|
||||
ctrl.second->setBounds(globalBounds);
|
||||
}
|
||||
ctrl.second->draw();
|
||||
ctrl.second->setBounds(localBounds);
|
||||
}
|
||||
drawFrame(2, frameStyle);
|
||||
}
|
||||
|
||||
void cTilemap::postChildrenResolve() {
|
||||
if(childrenPositioned) return cContainer::postChildrenResolve();
|
||||
rectangle saveFrame = getBounds();
|
||||
recalcRect(); // Calculate the rect with all cells on top of each other
|
||||
cellWidth = frame.width() + spacing;
|
||||
cellHeight = frame.height() + spacing;
|
||||
std::vector<cLedGroup*> groups;
|
||||
for(auto p : controls) {
|
||||
if(auto group = dynamic_cast<cLedGroup*>(p.second)) {
|
||||
groups.push_back(group);
|
||||
continue;
|
||||
}
|
||||
rectangle bounds = p.second->getBounds();
|
||||
bounds.offset(saveFrame.topLeft());
|
||||
bounds.offset(getCell(p.first));
|
||||
p.second->setBounds(bounds);
|
||||
}
|
||||
for(auto group : groups) {
|
||||
group->forEach([this, saveFrame](std::string id, cControl& ctrl) {
|
||||
rectangle bounds = ctrl.getBounds();
|
||||
bounds.offset(saveFrame.topLeft());
|
||||
bounds.offset(getCell(id));
|
||||
ctrl.setBounds(bounds);
|
||||
});
|
||||
group->recalcRect();
|
||||
}
|
||||
childrenPositioned = true;
|
||||
recalcRect(); // And calculate it again now that the controls are all moved into place
|
||||
}
|
||||
|
||||
void cTilemap::recalcRect() {
|
||||
auto iter = controls.begin();
|
||||
auto location = frame.topLeft();
|
||||
@@ -148,6 +165,13 @@ void cTilemap::recalcRect() {
|
||||
while(iter != controls.end()){
|
||||
cControl& ctrl = *iter->second;
|
||||
rectangle otherFrame = ctrl.getBounds();
|
||||
if(auto group = dynamic_cast<cLedGroup*>(&ctrl)) {
|
||||
group->recalcRect();
|
||||
// Groups outset the rect by -6. Inset it by 6 to compensate.
|
||||
rectangle bounds = group->getBounds();
|
||||
bounds.inset(6, 6);
|
||||
otherFrame = bounds;
|
||||
}
|
||||
if(otherFrame.right > frame.right)
|
||||
frame.right = otherFrame.right;
|
||||
if(otherFrame.bottom > frame.bottom)
|
||||
@@ -158,15 +182,9 @@ void cTilemap::recalcRect() {
|
||||
frame.top = otherFrame.top;
|
||||
iter++;
|
||||
}
|
||||
frame.offset(location);
|
||||
frame.right += spacing;
|
||||
frame.bottom += spacing;
|
||||
cellWidth = frame.width();
|
||||
cellHeight = frame.height();
|
||||
frame.width() *= cols;
|
||||
frame.height() *= rows;
|
||||
frame.right -= spacing;
|
||||
frame.bottom -= spacing;
|
||||
if(!childrenPositioned) {
|
||||
frame.offset(location);
|
||||
}
|
||||
}
|
||||
|
||||
void cTilemap::attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyMod)> handler, std::string prefix) {
|
||||
@@ -188,6 +206,25 @@ void cTilemap::attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyM
|
||||
}
|
||||
}
|
||||
|
||||
void cTilemap::attachFocusHandlers(std::function<bool(cDialog&,std::string,bool)> handler, std::string prefix) {
|
||||
for(auto& ctrl : controls) {
|
||||
bool matches = false;
|
||||
if(prefix.empty()) {
|
||||
size_t first_x = ctrl.first.find_first_of("x"), last_x = ctrl.first.find_last_of("x"), first_y = ctrl.first.find_first_of("y"), last_y = ctrl.first.find_last_of("y");
|
||||
if(first_x == 0 && last_x == 0 && first_y != std::string::npos && first_y == last_y) {
|
||||
matches = true;
|
||||
}
|
||||
} else {
|
||||
if(ctrl.first.compare(0, prefix.size(), prefix) == 0) {
|
||||
matches = true;
|
||||
}
|
||||
}
|
||||
if(matches) {
|
||||
ctrl.second->attachFocusHandler(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cTilemap::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs) {
|
||||
for(auto p : controls) {
|
||||
cControl& ctrl = *p.second;
|
||||
@@ -236,31 +273,71 @@ bool cTilemap::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::
|
||||
return cContainer::parseAttribute(attr, tagName, fname);
|
||||
}
|
||||
|
||||
template<typename Fcn>
|
||||
bool cTilemap::parseInstances(Fcn parse) {
|
||||
for(size_t x = 0; x < cols; x++) {
|
||||
for(size_t y = 0; y < rows; y++) {
|
||||
id_tries = 0;
|
||||
std::ostringstream sout;
|
||||
sout << "x" << x << "y" << y;
|
||||
current_cell = sout.str();
|
||||
std::pair<std::string, cControl*> p = parse();
|
||||
if(p.second == nullptr) return false;
|
||||
std::string anchor;
|
||||
auto pos = p.second->getPositioning(&anchor);
|
||||
if(!anchor.empty()) {
|
||||
// Relative positioning was requested. Assume it references another control within the same tilemap, and append the cell suffix.
|
||||
// TODO: Support relative anchors too?
|
||||
// TODO: Should it support anchoring to something outside the tilemap? Or is that too tricky?
|
||||
anchor.push_back('-');
|
||||
anchor += current_cell;
|
||||
p.second->setPositioning(anchor, pos.first, pos.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cTilemap::parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) {
|
||||
using namespace ticpp;
|
||||
if(content.Type() == TiXmlNode::ELEMENT) {
|
||||
std::string id;
|
||||
auto& elem = dynamic_cast<Element&>(content);
|
||||
id_tries = 0;
|
||||
current_cell = "x0y0";
|
||||
if(!parseChildControl(elem, controls, id, fname)) return false;
|
||||
for(size_t x = 0; x < cols; x++) {
|
||||
for(size_t y = 0; y < rows; y++) {
|
||||
if(x == 0 && y == 0) continue; // already did this one
|
||||
id_tries = 0;
|
||||
std::ostringstream sout;
|
||||
sout << "x" << x << "y" << y;
|
||||
current_cell = sout.str();
|
||||
parseChildControl(elem, controls, id, fname);
|
||||
// There's a special case for LED groups...
|
||||
if(elem.Value() == "mapgroup") {
|
||||
cLedGroup* group = new cLedGroup(*this);
|
||||
Iterator<Attribute> attr;
|
||||
for(attr = attr.begin(&elem); attr != attr.end(); attr++) {
|
||||
if(!group->parseAttribute(*attr, tagName, fname))
|
||||
throw xBadAttr(elem.Value(), attr->Name(), attr->Row(), attr->Column(), fname);
|
||||
}
|
||||
auto id = elem.GetAttribute("name");
|
||||
if(id.empty()) id = cControl::generateRandomString();
|
||||
group->setName(id);
|
||||
controls.emplace(id, group);
|
||||
Iterator<Element> iter;
|
||||
for(iter = iter.begin(&elem); iter != iter.end(); iter++) {
|
||||
parseInstances([this, &iter, group]{
|
||||
// TODO: The parent should really be group, not this? But passing this is needed to make the tilemap's generateId be called...
|
||||
auto p = getDialog()->parse<cLed>(*iter, *this);
|
||||
group->addChoice(p.second, p.first);
|
||||
return p;
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return parseInstances([this, &elem, &fname]{
|
||||
std::string id;
|
||||
bool result = parseChildControl(elem, controls, id, fname);
|
||||
if(result) return *controls.find(id);
|
||||
static const std::pair<const std::string, cControl*> nil;
|
||||
return nil;
|
||||
});
|
||||
}
|
||||
return cContainer::parseContent(content, n, tagName, fname, text);
|
||||
}
|
||||
|
||||
void cTilemap::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
|
||||
cControl::validatePostParse(who, fname, attrs, nodes);
|
||||
cContainer::validatePostParse(who, fname, attrs, nodes);
|
||||
if(!attrs.count("rows")) throw xMissingAttr(who.Value(), "rows", who.Row(), who.Column(), fname);
|
||||
if(!attrs.count("cols")) throw xMissingAttr(who.Value(), "cols", who.Row(), who.Column(), fname);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
class cTilemap : public cContainer {
|
||||
std::map<std::string,cControl*> controls;
|
||||
size_t rows, cols, spacing = 0, cellWidth, cellHeight;
|
||||
bool childrenPositioned = false;
|
||||
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
|
||||
location getCell(const std::string& id) const;
|
||||
location getCellIdx(const std::string& id) const;
|
||||
static std::string buildId(const std::string& base, size_t x, size_t y);
|
||||
std::string current_cell;
|
||||
mutable int id_tries = 0;
|
||||
bool isHandlingClick = false;
|
||||
template<typename Fcn>
|
||||
bool parseInstances(Fcn parse);
|
||||
public:
|
||||
std::string generateId(const std::string& baseId) const override;
|
||||
bool parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) override;
|
||||
@@ -37,6 +40,7 @@ public:
|
||||
cControl& getChild(size_t x, size_t y);
|
||||
location getCellPos(size_t x, size_t y) const;
|
||||
location getCellPos(cControl& child) const;
|
||||
location getCellIdx(cControl& child) const;
|
||||
/// Attach the same click handler to every instance of a child control.
|
||||
/// @param handler The handler to attach.
|
||||
/// @param prefix The unique ID of the control template.
|
||||
@@ -45,7 +49,14 @@ public:
|
||||
/// @see cControl::attachClickHandler()
|
||||
/// @deprecated in favour of @ref attachEventHandlers
|
||||
void attachClickHandlers(std::function<bool(cDialog&,std::string,eKeyMod)> handler, std::string prefix);
|
||||
bool handleClick(location where, cFramerateLimiter& fps_limiter) override;
|
||||
/// Attach the same focus handler to every instance of a child control.
|
||||
/// @param handler The handler to attach.
|
||||
/// @param prefix The unique ID of the control template.
|
||||
/// @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::string prefix);
|
||||
/// Recalculate the tilemap's bounding rect based on its contained controls.
|
||||
void recalcRect() override;
|
||||
/// Adds any fields in this tilemap to the tab order building arrays.
|
||||
@@ -60,7 +71,10 @@ public:
|
||||
std::set<eDlogEvt> getSupportedHandlers() const override {
|
||||
return {EVT_CLICK, EVT_FOCUS, EVT_DEFOCUS};
|
||||
}
|
||||
void postChildrenResolve() override;
|
||||
void forEach(std::function<void(std::string,cControl&)> callback) override;
|
||||
size_t getNumRows() const { return rows; }
|
||||
size_t getNumCols() const { return cols; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -302,6 +302,8 @@ The `<tilemap>` tag
|
||||
|
||||
The `<tilemap>` tag represents a grid of identical elements based off the provided template.
|
||||
It can contain any elements except for nested `<stack>`, `<pane>`, or `<tilemap>` elements.
|
||||
Every child element of the tilemap will be instanced for each cell of the tilemap,
|
||||
unless it is a `<mapgroup>` element (see below).
|
||||
|
||||
The `<tilemap>` tag accepts the following attributes:
|
||||
|
||||
@@ -310,6 +312,7 @@ The `<tilemap>` tag accepts the following attributes:
|
||||
* `rows` - The number of rows to generate. Required.
|
||||
* `cols` - The number of columns to generate. Required.
|
||||
* `cellspacing` - If specified, adds a buffer of this size between each cell.
|
||||
* `<mapgroup>` - Defines a combined LED group that will contain every instance of the LEDs it defines. which will be instanced for _each_ cell of the tilemap.
|
||||
|
||||
Keyboard Shortcuts
|
||||
------------------
|
||||
|
||||
@@ -965,6 +965,15 @@ void readScenarioFromXml(ticpp::Document&& data, cScenario& scenario) {
|
||||
if(itfnum > scenario.itf_names.size())
|
||||
scenario.itf_names.resize(itfnum);
|
||||
edit->GetText(&scenario.itf_names[itfnum - 1], false);
|
||||
} else if(type == "sdf") {
|
||||
int row, col;
|
||||
edit->GetAttribute("row", &row);
|
||||
if(row < 0 || row >= scenario.sdf_names.size())
|
||||
throw xBadVal(type, "row", std::to_string(row), edit->Row(), edit->Column(), fname);
|
||||
edit->GetAttribute("col", &col);
|
||||
if(col < 0 || col >= scenario.sdf_names[0].size())
|
||||
throw xBadVal(type, "col", std::to_string(col), edit->Row(), edit->Column(), fname);
|
||||
edit->GetText(&scenario.sdf_names[row][col]);
|
||||
} else if(type == "graphics") {
|
||||
static const std::set<ePicType> valid_pictypes = {
|
||||
PIC_TER, PIC_TER_ANIM, PIC_TER_MAP,
|
||||
|
||||
@@ -205,6 +205,7 @@ void swap(cScenario& lhs, cScenario& rhs) {
|
||||
swap(lhs.evt_names, rhs.evt_names);
|
||||
swap(lhs.ic_names, rhs.ic_names);
|
||||
swap(lhs.itf_names, rhs.itf_names);
|
||||
swap(lhs.sdf_names, rhs.sdf_names);
|
||||
}
|
||||
|
||||
cScenario::cItemStorage::cItemStorage() : ter_type(-1), property(0) {
|
||||
|
||||
@@ -39,6 +39,10 @@ struct scenario_header_flags {
|
||||
|
||||
enum eContentRating {G, PG, R, NC17};
|
||||
|
||||
// TODO: Duplicated in party.hpp
|
||||
template<typename T, size_t x, size_t y>
|
||||
using array2d = std::array<std::array<T, y>, x>;
|
||||
|
||||
// Used for finding town entrances in the outdoors
|
||||
struct town_entrance_t {
|
||||
location out_sec;
|
||||
@@ -103,6 +107,7 @@ public:
|
||||
std::vector<std::string> evt_names;
|
||||
std::vector<std::string> ic_names;
|
||||
std::vector<std::string> itf_names;
|
||||
array2d<std::string, 350, 50> sdf_names;
|
||||
bool adjust_diff;
|
||||
bool is_legacy;
|
||||
fs::path scen_file; // transient
|
||||
|
||||
@@ -10,6 +10,7 @@ scened_sources = Split("""
|
||||
scen.keydlgs.cpp
|
||||
scen.locpicker.cpp
|
||||
scen.main.cpp
|
||||
scen.sdfpicker.cpp
|
||||
scen.townout.cpp
|
||||
../view_dialogs.cpp
|
||||
../fileio/fileio_party.cpp
|
||||
|
||||
@@ -415,6 +415,16 @@ void writeScenarioToXml(ticpp::Printer&& data, cScenario& scenario) {
|
||||
data.PushText(scenario.itf_names[i]);
|
||||
data.CloseElement("item-typeflag");
|
||||
}
|
||||
for(int x = 0; x < scenario.sdf_names.size(); x++) {
|
||||
for(int y = 0; y < scenario.sdf_names[x].size(); y++) {
|
||||
if(scenario.sdf_names[x][y].empty()) continue;
|
||||
data.OpenElement("sdf");
|
||||
data.PushAttribute("row", x);
|
||||
data.PushAttribute("col", y);
|
||||
data.PushText(scenario.sdf_names[x][y]);
|
||||
data.CloseElement("sdf");
|
||||
}
|
||||
}
|
||||
data.CloseElement("editor");
|
||||
data.CloseElement("scenario");
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "scen.graphics.hpp"
|
||||
#include "scen.keydlgs.hpp"
|
||||
#include "scen.locpicker.hpp"
|
||||
#include "scen.sdfpicker.hpp"
|
||||
#include "scen.core.hpp"
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
#include "dialogxml/widgets/control.hpp"
|
||||
@@ -1118,6 +1119,16 @@ static bool edit_spec_enc_value(cDialog& me, std::string item_hit, node_stack_t&
|
||||
store = loc.x;
|
||||
me[otherField].setTextToNum(loc.y);
|
||||
} break;
|
||||
case eSpecPicker::SDF: {
|
||||
// NOTE: Yes, it's correct that the main field has y and the "other field" has x.
|
||||
auto otherField = get_control_for_field(fcn.continuation);
|
||||
location sdf;
|
||||
sdf.y = val;
|
||||
sdf.x = me[otherField].getTextAsNum();
|
||||
sdf = cStuffDonePicker(sdf).run();
|
||||
store = sdf.y;
|
||||
me[otherField].setTextToNum(sdf.x);
|
||||
} break;
|
||||
case eSpecPicker::FIELD: store = choose_field_type(val, &me, fcn.augmented); break;
|
||||
case eSpecPicker::DAMAGE_TYPE: store = choose_damage_type(val, &me, true); break;
|
||||
case eSpecPicker::EXPLOSION: store = choose_boom_type(val, &me); break;
|
||||
|
||||
@@ -110,8 +110,8 @@ bool cLocationPicker::handle_scroll(std::string item_hit) {
|
||||
}
|
||||
|
||||
bool cLocationPicker::handle_select(std::string item_hit) {
|
||||
location clickedLoc = map->getCellPos(dlog[item_hit]);
|
||||
location check{viewport.x + clickedLoc.x / 24, viewport.y + clickedLoc.y / 24};
|
||||
location clickedLoc = map->getCellIdx(dlog[item_hit]);
|
||||
location check{viewport.x + clickedLoc.x, viewport.y + clickedLoc.y};
|
||||
if(check.x < area->max_dim && check.y < area->max_dim) {
|
||||
chosen_loc = check;
|
||||
place_pointer();
|
||||
|
||||
164
src/scenedit/scen.sdfpicker.cpp
Normal file
164
src/scenedit/scen.sdfpicker.cpp
Normal file
@@ -0,0 +1,164 @@
|
||||
//
|
||||
// scen.sdfpicker.cpp
|
||||
// BoE Scenario Editor
|
||||
//
|
||||
// Created by Celtic Minstrel on 2025-03-02.
|
||||
//
|
||||
|
||||
#include "scen.sdfpicker.hpp"
|
||||
|
||||
#include "dialogxml/widgets/field.hpp"
|
||||
#include "dialogxml/widgets/ledgroup.hpp"
|
||||
#include "dialogxml/widgets/tilemap.hpp"
|
||||
#include "fileio/resmgr/res_dialog.hpp"
|
||||
#include "scenario/scenario.hpp"
|
||||
#include "sounds.hpp"
|
||||
|
||||
extern cScenario scenario;
|
||||
|
||||
cStuffDonePicker::cStuffDonePicker(location sdf)
|
||||
: dlog(*ResMgr::dialogs.get("choose-sdf"))
|
||||
, initial_sdf(sdf)
|
||||
, chosen_sdf(sdf)
|
||||
{
|
||||
grid = dynamic_cast<cTilemap*>(&dlog["map"]);
|
||||
row_labels = dynamic_cast<cTilemap*>(&dlog["rows"]);
|
||||
col_labels = dynamic_cast<cTilemap*>(&dlog["cols"]);
|
||||
rows = grid->getNumRows();
|
||||
cols = grid->getNumCols();
|
||||
clamp_sdf();
|
||||
fill_names();
|
||||
handle_scroll(""); // Hide unusable scroll buttons
|
||||
using namespace std::placeholders;
|
||||
dlog.attachClickHandlers(std::bind(&cStuffDonePicker::handle_close, this, _2), {"done", "cancel"});
|
||||
dlog.attachClickHandlers(std::bind(&cStuffDonePicker::handle_scroll, this, _2), {"up", "down", "left", "right"});
|
||||
grid->attachFocusHandlers(std::bind(&cStuffDonePicker::handle_focus, this, _2, _3), "name");
|
||||
dlog["choice"].attachFocusHandler(std::bind(&cStuffDonePicker::handle_select, this, _3));
|
||||
}
|
||||
|
||||
bool cStuffDonePicker::handle_close(std::string item_hit) {
|
||||
dlog.toast(item_hit == "done");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cStuffDonePicker::handle_scroll(std::string item_hit) {
|
||||
if(!item_hit.empty()) save_names();
|
||||
if(item_hit == "up") {
|
||||
if(viewport.y > 0) viewport.y -= rows;
|
||||
} else if(item_hit == "down") {
|
||||
if(viewport.y < scenario.sdf_names[0].size() - rows) viewport.y += rows;
|
||||
} else if(item_hit == "left") {
|
||||
if(viewport.x > 0) viewport.x -= cols;
|
||||
} else if(item_hit == "right") {
|
||||
if(viewport.x < scenario.sdf_names.size() - cols) viewport.x += cols;
|
||||
}
|
||||
if(viewport.x == 0) dlog["left"].hide();
|
||||
else dlog["left"].show();
|
||||
if(viewport.y == 0) dlog["up"].hide();
|
||||
else dlog["up"].show();
|
||||
if(viewport.x >= scenario.sdf_names.size() - cols) dlog["right"].hide();
|
||||
else dlog["right"].show();
|
||||
if(viewport.y >= scenario.sdf_names[0].size() - rows) dlog["down"].hide();
|
||||
else dlog["down"].show();
|
||||
fill_names();
|
||||
if(!item_hit.empty()) select_active();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cStuffDonePicker::handle_select(bool losing) {
|
||||
if(losing) return true;
|
||||
std::string item_hit = dynamic_cast<cLedGroup&>(dlog["choice"]).getSelected();
|
||||
location clickedFlag = grid->getCellIdx(dlog[item_hit]);
|
||||
chosen_sdf = {viewport.x + clickedFlag.x, viewport.y + clickedFlag.y};
|
||||
auto& field = dynamic_cast<cTextField&>(grid->getChild("name", clickedFlag.x, clickedFlag.y));
|
||||
skipNextFocus = true;
|
||||
dlog.setFocus(&field);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cStuffDonePicker::handle_focus(std::string item_hit, bool losing) {
|
||||
if(losing) return true;
|
||||
if(skipNextFocus) {
|
||||
skipNextFocus = false;
|
||||
return true;
|
||||
}
|
||||
auto& field = dlog[item_hit];
|
||||
if(!field.getText().empty()) return true;
|
||||
location clickedFlag = grid->getCellIdx(field);
|
||||
chosen_sdf = {viewport.x + clickedFlag.x, viewport.y + clickedFlag.y};
|
||||
play_sound(34);
|
||||
auto& group = dynamic_cast<cLedGroup&>(dlog["choice"]);
|
||||
// TODO: It would be nice to do it like this, but getChild currently doesn't recurse to children of children.
|
||||
// auto& led = grid->getChild("select", clickedFlag.x, clickedFlag.y);
|
||||
// group.setSelected(led.getName());
|
||||
auto name = field.getName();
|
||||
auto hyphen = name.find_last_of("-");
|
||||
group.setSelected("select" + name.substr(hyphen));
|
||||
return true;
|
||||
}
|
||||
|
||||
location cStuffDonePicker::run() {
|
||||
skipNextFocus = true;
|
||||
dlog.run([this](cDialog&) { select_active(); });
|
||||
save_names();
|
||||
if(dlog.accepted()) return chosen_sdf;
|
||||
return initial_sdf;
|
||||
}
|
||||
|
||||
void cStuffDonePicker::clamp_sdf() {
|
||||
chosen_sdf.x = minmax(0, scenario.sdf_names.size() - 1, chosen_sdf.x);
|
||||
chosen_sdf.y = minmax(0, scenario.sdf_names[0].size() - 1, chosen_sdf.y);
|
||||
viewport.x = cols * floor(chosen_sdf.x / float(cols));
|
||||
viewport.y = rows * floor(chosen_sdf.y / float(rows));
|
||||
}
|
||||
|
||||
void cStuffDonePicker::fill_names() {
|
||||
for(int x = 0; x < cols; x++) {
|
||||
for(int y = 0; y < rows; y++) {
|
||||
auto& field = grid->getChild("name", x, y);
|
||||
location sdf = viewport;
|
||||
sdf.x += x;
|
||||
sdf.y += y;
|
||||
field.setText(scenario.sdf_names[sdf.x][sdf.y]);
|
||||
if(x == 0) {
|
||||
// Add row labels
|
||||
row_labels->getChild("row", 0, y).setTextToNum(sdf.y);
|
||||
}
|
||||
if(y == 0) {
|
||||
// Add column labels
|
||||
col_labels->getChild("col", x, 0).setTextToNum(sdf.x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cStuffDonePicker::save_names() {
|
||||
for(int x = 0; x < cols; x++) {
|
||||
for(int y = 0; y < rows; y++) {
|
||||
auto& field = grid->getChild("name", x, y);
|
||||
location sdf = viewport;
|
||||
sdf.x += x;
|
||||
sdf.y += y;
|
||||
scenario.sdf_names[sdf.x][sdf.y] = field.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cStuffDonePicker::select_active() {
|
||||
auto& group = dynamic_cast<cLedGroup&>(dlog["choice"]);
|
||||
if(chosen_sdf.x >= viewport.x && chosen_sdf.x < viewport.x + cols && chosen_sdf.y >= viewport.y && chosen_sdf.y < viewport.y + rows) {
|
||||
location relative_sdf = chosen_sdf;
|
||||
relative_sdf.x -= viewport.x;
|
||||
relative_sdf.y -= viewport.y;
|
||||
auto& field = dynamic_cast<cTextField&>(grid->getChild("name", relative_sdf.x, relative_sdf.y));
|
||||
// TODO: It would be nice to do it like this, but getChild currently doesn't recurse to children of children.
|
||||
// auto& led = grid->getChild("select", relative_sdf.x, relative_sdf.y);
|
||||
// group.setSelected(led.getName());
|
||||
auto name = field.getName();
|
||||
auto hyphen = name.find_last_of("-");
|
||||
group.setSelected("select" + name.substr(hyphen));
|
||||
dlog.setFocus(&field);
|
||||
} else {
|
||||
group.setSelected("");
|
||||
}
|
||||
}
|
||||
38
src/scenedit/scen.sdfpicker.hpp
Normal file
38
src/scenedit/scen.sdfpicker.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// scen.sdfpicker.hpp
|
||||
// BoE Scenario Editor
|
||||
//
|
||||
// Created by Celtic Minstrel on 2025-03-02.
|
||||
//
|
||||
|
||||
#ifndef BoE_scen_sdfpicker_h
|
||||
#define BoE_scen_sdfpicker_h
|
||||
|
||||
#include <string>
|
||||
#include "location.hpp"
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
|
||||
class cTilemap;
|
||||
|
||||
class cStuffDonePicker {
|
||||
cDialog dlog;
|
||||
location initial_sdf, chosen_sdf, viewport;
|
||||
cTilemap* grid;
|
||||
cTilemap* row_labels;
|
||||
cTilemap* col_labels;
|
||||
int rows, cols;
|
||||
bool skipNextFocus = true;
|
||||
void clamp_sdf();
|
||||
void fill_names();
|
||||
void save_names();
|
||||
void select_active();
|
||||
bool handle_close(std::string item_hit);
|
||||
bool handle_scroll(std::string item_hit);
|
||||
bool handle_select(bool losing);
|
||||
bool handle_focus(std::string item_hit, bool losing);
|
||||
public:
|
||||
cStuffDonePicker(location sdf);
|
||||
location run();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -36,6 +36,7 @@ namespace legacy {
|
||||
struct setup_save_type;
|
||||
};
|
||||
|
||||
// TODO: Duplicated in scenario.hpp
|
||||
template<typename T, size_t x, size_t y>
|
||||
using array2d = std::array<std::array<T, y>, x>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user