Add a new tilemap control that replicates its children into a fixed grid.
Use it for the Edit Terrain Object dialog.
This commit is contained in:
@@ -211,6 +211,7 @@
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\scrollbar.cpp" />
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\scrollpane.cpp" />
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\stack.cpp" />
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\tilemap.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\debugout.win.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\estreams.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\fileio.cpp" />
|
||||
@@ -302,6 +303,7 @@
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\scrollbar.hpp" />
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\scrollpane.hpp" />
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\stack.hpp" />
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\tilemap.hpp" />
|
||||
<ClInclude Include="..\..\..\src\fields.hpp" />
|
||||
<ClInclude Include="..\..\..\src\fileio\fileio.hpp" />
|
||||
<ClInclude Include="..\..\..\src\fileio\gzstream\gzstream.h" />
|
||||
|
||||
@@ -764,6 +764,9 @@
|
||||
<ClCompile Include="..\..\..\src\dialogxml\keycodes.cpp">
|
||||
<Filter>DialogXML</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\tilemap.cpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\stack.cpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClCompile>
|
||||
@@ -974,6 +977,9 @@
|
||||
<ClInclude Include="..\..\..\src\fileio\gzstream\gzstream.h">
|
||||
<Filter>FileIO\GZStream</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\tilemap.hpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\stack.hpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClInclude>
|
||||
|
||||
@@ -363,6 +363,7 @@
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\scrollbar.cpp" />
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\scrollpane.cpp" />
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\stack.cpp" />
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\tilemap.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\debugout.win.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\estreams.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\fileio.cpp" />
|
||||
@@ -459,6 +460,7 @@
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\scrollbar.hpp" />
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\scrollpane.hpp" />
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\stack.hpp" />
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\tilemap.hpp" />
|
||||
<ClInclude Include="..\..\..\src\fields.hpp" />
|
||||
<ClInclude Include="..\..\..\src\fileio\fileio.hpp" />
|
||||
<ClInclude Include="..\..\..\src\fileio\gzstream\gzstream.h" />
|
||||
|
||||
@@ -802,6 +802,9 @@
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\stack.cpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\dialogxml\widgets\tilemap.cpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\src\fileio\resmgr\res_cursor.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\resmgr\res_dialog.cpp" />
|
||||
<ClCompile Include="..\..\..\src\fileio\resmgr\res_font.cpp" />
|
||||
@@ -1052,6 +1055,9 @@
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\stack.hpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\dialogxml\widgets\tilemap.hpp">
|
||||
<Filter>DialogXML\Widgets</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\src\tools\replay.hpp" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -164,6 +164,7 @@
|
||||
919BE8A02D676DDE000C64C6 /* special-town.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 919BE89F2D676DDE000C64C6 /* special-town.cpp */; };
|
||||
919BE8AC2D677699000C64C6 /* special-rect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 919BE8AB2D677699000C64C6 /* special-rect.cpp */; };
|
||||
919BE8B32D6776A8000C64C6 /* special-outdoor.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 919BE8B22D6776A8000C64C6 /* special-outdoor.cpp */; };
|
||||
919BE9162D6F5813000C64C6 /* tilemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 913D1E3F2D4E97E4007D46B2 /* tilemap.cpp */; };
|
||||
919CC2481B3772F300273FDA /* population.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91AC620A0FA2853700EEAE67 /* population.cpp */; };
|
||||
919CC2491B3772FB00273FDA /* creature.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 914698FE1A747C4500F20F5E /* creature.cpp */; };
|
||||
919CC24B1B37730300273FDA /* item.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 91279D3D0F9D1D6A007B0D52 /* item.cpp */; };
|
||||
@@ -684,6 +685,8 @@
|
||||
913D05B50FA1E9E300184C18 /* party.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = party.cpp; sourceTree = "<group>"; };
|
||||
913D05BA0FA1EA0A00184C18 /* pc.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = pc.hpp; sourceTree = "<group>"; };
|
||||
913D05BB0FA1EA0A00184C18 /* pc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pc.cpp; sourceTree = "<group>"; };
|
||||
913D1E3F2D4E97E4007D46B2 /* tilemap.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = tilemap.cpp; sourceTree = "<group>"; };
|
||||
913D1E402D4E97E4007D46B2 /* tilemap.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = tilemap.hpp; sourceTree = "<group>"; };
|
||||
913D6C040FC57A8E00E12527 /* boeresources.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = boeresources.icns; path = icons/mac/boeresources.icns; sourceTree = "<group>"; };
|
||||
913FB40A1A5C90840067B9D2 /* pictypes.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = pictypes.hpp; sourceTree = "<group>"; };
|
||||
91430437296C0088003A3967 /* vector2d.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = vector2d.cpp; sourceTree = "<group>"; };
|
||||
@@ -1093,6 +1096,7 @@
|
||||
9191460018E63D8E005CF3A4 /* scrollbar.cpp */,
|
||||
919B13A81BBE2B54009905A4 /* scrollpane.cpp */,
|
||||
9179A4641A48681800FEF872 /* stack.cpp */,
|
||||
913D1E3F2D4E97E4007D46B2 /* tilemap.cpp */,
|
||||
910BBA880FB8EC57001E34EA /* button.hpp */,
|
||||
9149924625913E3F00B5BE97 /* container.hpp */,
|
||||
910BBA3B0FB8DA8E001E34EA /* control.hpp */,
|
||||
@@ -1105,6 +1109,7 @@
|
||||
919145FD18E3C750005CF3A4 /* scrollbar.hpp */,
|
||||
919B13A71BBE297B009905A4 /* scrollpane.hpp */,
|
||||
9179A4631A4867E200FEF872 /* stack.hpp */,
|
||||
913D1E402D4E97E4007D46B2 /* tilemap.hpp */,
|
||||
);
|
||||
path = widgets;
|
||||
sourceTree = "<group>";
|
||||
@@ -2107,6 +2112,7 @@
|
||||
919CC2621B37738A00273FDA /* ticpp.cpp in Sources */,
|
||||
919CC2631B37739000273FDA /* tinystr.cpp in Sources */,
|
||||
919CC2641B37739300273FDA /* tinyxml.cpp in Sources */,
|
||||
919BE9162D6F5813000C64C6 /* tilemap.cpp in Sources */,
|
||||
919CC2651B37739800273FDA /* tinyxmlerror.cpp in Sources */,
|
||||
919CC2661B37739E00273FDA /* tinyxmlparser.cpp in Sources */,
|
||||
413FE08F2CECFAFF000D97DC /* winutil.cpp in Sources */,
|
||||
|
||||
@@ -3,23 +3,9 @@
|
||||
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
|
||||
<dialog defbtn='okay'>
|
||||
<pict type='dlog' num='16' top='8' left='8'/>
|
||||
<text framed='true' top='12' left='60' width='112' height='144'/>
|
||||
<pict name='x0y0' framed='false' type='ter' num='0' top='12' left='60'/>
|
||||
<pict name='x0y1' framed='false' type='ter' num='0' top='48' left='60'/>
|
||||
<pict name='x0y2' framed='false' type='ter' num='0' top='84' left='60'/>
|
||||
<pict name='x0y3' framed='false' type='ter' num='0' top='120' left='60'/>
|
||||
<pict name='x1y0' framed='false' type='ter' num='0' top='12' left='88'/>
|
||||
<pict name='x1y1' framed='false' type='ter' num='0' top='48' left='88'/>
|
||||
<pict name='x1y2' framed='false' type='ter' num='0' top='84' left='88'/>
|
||||
<pict name='x1y3' framed='false' type='ter' num='0' top='120' left='88'/>
|
||||
<pict name='x2y0' framed='false' type='ter' num='0' top='12' left='116'/>
|
||||
<pict name='x2y1' framed='false' type='ter' num='0' top='48' left='116'/>
|
||||
<pict name='x2y2' framed='false' type='ter' num='0' top='84' left='116'/>
|
||||
<pict name='x2y3' framed='false' type='ter' num='0' top='120' left='116'/>
|
||||
<pict name='x3y0' framed='false' type='ter' num='0' top='12' left='144'/>
|
||||
<pict name='x3y1' framed='false' type='ter' num='0' top='48' left='144'/>
|
||||
<pict name='x3y2' framed='false' type='ter' num='0' top='84' left='144'/>
|
||||
<pict name='x3y3' framed='false' type='ter' num='0' top='120' left='144'/>
|
||||
<tilemap name='map' framed='true' top='12' left='60' rows='4' cols='4'>
|
||||
<pict framed='false' type='ter' num='0' top='0' left='0'/>
|
||||
</tilemap>
|
||||
<text top='166' left='130' width='10' height='16'>X</text>
|
||||
<text top='166' left='180' width='10' height='16'>Y</text>
|
||||
<text top='186' left='10' width='100' height='16'>Object Size:</text>
|
||||
|
||||
@@ -298,6 +298,24 @@
|
||||
<xs:attributeGroup ref="frame"/>
|
||||
<xs:attribute name="pages" type="xs:integer"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="tilemap">
|
||||
<xs:choice minOccurs="1" maxOccurs="unbounded">
|
||||
<xs:element name="field" type="field"/>
|
||||
<xs:element name="text" type="message"/>
|
||||
<xs:element name="pict" type="pict"/>
|
||||
<xs:element name="button" type="button"/>
|
||||
<xs:element name="led" type="led"/>
|
||||
<xs:element name="group" type="ledGroup"/>
|
||||
<xs:element name="slider" type="slider"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:token"/>
|
||||
<xs:attributeGroup ref="frame"/>
|
||||
<xs:attribute name="rows" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="cols" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="cellspacing" type="xs:integer"/>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="scrollPane">
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="field" type="field"/>
|
||||
@@ -334,6 +352,7 @@
|
||||
<xs:field xpath="@default"/>
|
||||
</xs:unique>
|
||||
</xs:element>
|
||||
<xs:element name="tilemap" type="tilemap"/>
|
||||
<xs:element name="pane" type="scrollPane"/>
|
||||
</xs:choice>
|
||||
<xs:attribute name="skin" default="dark">
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "dialogxml/widgets/scrollbar.hpp"
|
||||
#include "dialogxml/widgets/scrollpane.hpp"
|
||||
#include "dialogxml/widgets/stack.hpp"
|
||||
#include "dialogxml/widgets/tilemap.hpp"
|
||||
#include "tools/keymods.hpp"
|
||||
#include "tools/winutil.hpp"
|
||||
#include "mathutil.hpp"
|
||||
@@ -45,22 +46,14 @@ const short cDialog::BG_DARK = 5, cDialog::BG_LIGHT = 16;
|
||||
short cDialog::defaultBackground = cDialog::BG_DARK;
|
||||
cDialog* cDialog::topWindow = nullptr;
|
||||
void (*cDialog::redraw_everything)() = nullptr;
|
||||
std::mt19937 cDialog::ui_rand;
|
||||
|
||||
extern std::map<std::string,sf::Color> colour_map;
|
||||
|
||||
extern bool check_for_interrupt(std::string);
|
||||
extern void showError(std::string str1, cDialog* parent = nullptr);
|
||||
|
||||
std::string cDialog::generateRandomString(){
|
||||
// Not bothering to seed, because it doesn't actually matter if it's truly random.
|
||||
int n_chars = ui_rand() % 100;
|
||||
std::string s = "$";
|
||||
while(n_chars > 0){
|
||||
s += char(ui_rand() % 96) + ' '; // was 223 ...
|
||||
n_chars--;
|
||||
}
|
||||
return s;
|
||||
std::string cDialog::generateId(const std::string& explicitId) const {
|
||||
return explicitId.empty() ? cControl::generateRandomString() : explicitId;
|
||||
}
|
||||
|
||||
string cControl::dlogStringFilter(string toFilter) {
|
||||
@@ -235,6 +228,10 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
||||
inserted = controls.insert(parsed).first;
|
||||
// TODO: Now, if it contains any fields, their tab order must be accounted for
|
||||
//parsed.second->fillTabOrder(specificTabs, reverseTabs);
|
||||
} else if(type == "tilemap") {
|
||||
auto parsed = parse<cTilemap>(*node);
|
||||
inserted = controls.insert(parsed).first;
|
||||
parsed.second->fillTabOrder(specificTabs, reverseTabs);
|
||||
} else throw xBadNode(type,node->Row(),node->Column(),fname);
|
||||
if(prevCtrl.second) {
|
||||
if(inserted->second->anchor == "$$prev$$" && prevCtrl.second->anchor == "$$next$$") {
|
||||
@@ -271,6 +268,7 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
||||
case CTRL_STACK: ctrlType = "stack"; break;
|
||||
case CTRL_SCROLL: ctrlType = "slider"; break;
|
||||
case CTRL_PANE: ctrlType = "pane"; break;
|
||||
case CTRL_MAP: ctrlType = "tilemap"; break;
|
||||
}
|
||||
throw xBadVal(ctrlType, "anchor", ctrl.anchor, 0, 0, fname);
|
||||
}
|
||||
@@ -293,6 +291,7 @@ void cDialog::loadFromFile(const DialogDefn& file){
|
||||
case CTRL_STACK: ctrlType = "stack"; break;
|
||||
case CTRL_SCROLL: ctrlType = "slider"; break;
|
||||
case CTRL_PANE: ctrlType = "pane"; break;
|
||||
case CTRL_MAP: ctrlType = "tilemap"; break;
|
||||
}
|
||||
throw xBadVal(ctrlType, "anchor", "<circular dependency>", 0, 0, fname);
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <deque>
|
||||
#include <random>
|
||||
|
||||
#include "ticpp.h"
|
||||
#include "dialogxml/keycodes.hpp"
|
||||
@@ -68,14 +67,13 @@ class cDialog {
|
||||
std::string currentFocus;
|
||||
cDialog* parent;
|
||||
cControl* findControl(std::string id);
|
||||
std::string generateRandomString();
|
||||
std::string generateId(const std::string& explicitId) const;
|
||||
void loadFromFile(const DialogDefn& file);
|
||||
void handleTab(bool reverse);
|
||||
template<typename Iter> void handleTabOrder(std::string& itemHit, Iter begin, Iter end);
|
||||
std::vector<std::pair<std::string,cTextField*>> tabOrder;
|
||||
static cDialog* topWindow; // Tracks the frontmost dialog.
|
||||
static bool initCalled;
|
||||
static std::mt19937 ui_rand;
|
||||
public:
|
||||
static void (*redraw_everything)();
|
||||
/// Performs essential startup initialization. Generally should not be called directly.
|
||||
@@ -242,19 +240,21 @@ public:
|
||||
/// Adds a new control described by the passed XML element.
|
||||
/// @tparam Ctrl The type of control to add.
|
||||
/// @param who The XML element describing the control.
|
||||
/// @param parent The parent control, if any. Omit if there is no parent.
|
||||
/// @note It is up to the caller to ensure that that the element
|
||||
/// passed describes the type of control being requested.
|
||||
template<class Ctrl> std::pair<std::string,Ctrl*> parse(ticpp::Element& who) {
|
||||
template<class Ctrl, class Container> std::pair<std::string,Ctrl*> parse(ticpp::Element& who, Container* parent) {
|
||||
std::pair<std::string,Ctrl*> p;
|
||||
p.second = new Ctrl(*this);
|
||||
p.first = p.second->parse(who, fname);
|
||||
if(p.first == ""){
|
||||
do{
|
||||
p.first = generateRandomString();
|
||||
}while(controls.find(p.first) != controls.end());
|
||||
}
|
||||
do{
|
||||
p.first = parent->generateId(p.first);
|
||||
}while(controls.find(p.first) != controls.end());
|
||||
return p;
|
||||
}
|
||||
template<class Ctrl> std::pair<std::string,Ctrl*> parse(ticpp::Element& who) {
|
||||
return parse<Ctrl, cDialog>(who, this);
|
||||
}
|
||||
cDialogIterator begin() {
|
||||
return cDialogIterator(this);
|
||||
}
|
||||
|
||||
@@ -20,32 +20,32 @@ bool cContainer::parseChildControl(ticpp::Element& elem, std::map<std::string,cC
|
||||
ctrlIter inserted;
|
||||
std::string tag = elem.Value();
|
||||
if(tag == "field") {
|
||||
auto field = parent->parse<cTextField>(elem);
|
||||
auto field = parent->parse<cTextField>(elem, this);
|
||||
inserted = controls.insert(field).first;
|
||||
parent->tabOrder.push_back(field);
|
||||
id = field.first;
|
||||
} else if(tag == "text") {
|
||||
auto text = parent->parse<cTextMsg>(elem);
|
||||
auto text = parent->parse<cTextMsg>(elem, this);
|
||||
inserted = controls.insert(text).first;
|
||||
id = text.first;
|
||||
} else if(tag == "pict") {
|
||||
auto pict = parent->parse<cPict>(elem);
|
||||
auto pict = parent->parse<cPict>(elem, this);
|
||||
inserted = controls.insert(pict).first;
|
||||
id = pict.first;
|
||||
} else if(tag == "slider") {
|
||||
auto slide = parent->parse<cScrollbar>(elem);
|
||||
auto slide = parent->parse<cScrollbar>(elem, this);
|
||||
inserted = controls.insert(slide).first;
|
||||
id = slide.first;
|
||||
} else if(tag == "button") {
|
||||
auto button = parent->parse<cButton>(elem);
|
||||
auto button = parent->parse<cButton>(elem, this);
|
||||
inserted = controls.insert(button).first;
|
||||
id = button.first;
|
||||
} else if(tag == "led") {
|
||||
auto led = parent->parse<cLed>(elem);
|
||||
auto led = parent->parse<cLed>(elem, this);
|
||||
inserted = controls.insert(led).first;
|
||||
id = led.first;
|
||||
} else if(tag == "group") {
|
||||
auto group = parent->parse<cLedGroup>(elem);
|
||||
auto group = parent->parse<cLedGroup>(elem, this);
|
||||
inserted = controls.insert(group).first;
|
||||
id = group.first;
|
||||
} else return false;
|
||||
|
||||
@@ -23,6 +23,22 @@
|
||||
|
||||
// Hyperlink forward declaration
|
||||
extern void launchURL(std::string url);
|
||||
std::mt19937 cControl::ui_rand;
|
||||
|
||||
std::string cControl::generateId(const std::string& explicitId) const {
|
||||
return explicitId.empty() ? generateRandomString() : explicitId;
|
||||
}
|
||||
|
||||
std::string cControl::generateRandomString() {
|
||||
// Not bothering to seed, because it doesn't actually matter if it's truly random.
|
||||
int n_chars = ui_rand() % 100;
|
||||
std::string s = "$";
|
||||
while(n_chars > 0){
|
||||
s += char(ui_rand() % 96) + ' '; // was 223 ...
|
||||
n_chars--;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void cControl::setText(std::string l){
|
||||
lbl = l;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <random>
|
||||
#include <boost/any.hpp>
|
||||
#include "dialogxml/dialogs/dlogevt.hpp"
|
||||
#include "tools/framerate_limiter.hpp"
|
||||
@@ -62,6 +63,7 @@ enum eControlType {
|
||||
CTRL_STACK, ///< A group of controls that represents one element in an array
|
||||
CTRL_SCROLL,///< A scrollbar
|
||||
CTRL_PANE, ///< A scroll pane
|
||||
CTRL_MAP, ///< A 2-dimensional grid of identical controls
|
||||
};
|
||||
|
||||
enum ePosition {
|
||||
@@ -461,6 +463,11 @@ protected:
|
||||
void redraw();
|
||||
/// Plays the proper sound for this control being clicked on
|
||||
void playClickSound();
|
||||
/// Generate a unique ID for a control. The explicitId is the ID specified in the XML, if any.
|
||||
/// 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;
|
||||
static std::string generateRandomString();
|
||||
private:
|
||||
friend class cDialog; // This is so it can access parseColour and anchor
|
||||
friend class cContainer; // This is so it can access anchor
|
||||
@@ -472,6 +479,7 @@ private:
|
||||
ePosition horz = POS_ABS, vert = POS_ABS;
|
||||
std::string anchor;
|
||||
bool is_link = false;
|
||||
static std::mt19937 ui_rand;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
210
src/dialogxml/widgets/tilemap.cpp
Normal file
210
src/dialogxml/widgets/tilemap.cpp
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// tilemap.cpp
|
||||
// BoE
|
||||
//
|
||||
// Created by Celtic Minstrel on 2025-02-01.
|
||||
//
|
||||
|
||||
#include "tilemap.hpp"
|
||||
|
||||
#include "button.hpp"
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
#include "field.hpp"
|
||||
#include "message.hpp"
|
||||
#include "pict.hpp"
|
||||
#include "scrollbar.hpp"
|
||||
#include "stack.hpp"
|
||||
#include <climits>
|
||||
|
||||
std::string cTilemap::generateId(const std::string& baseId) const {
|
||||
if(baseId.empty()) {
|
||||
if(id_tries++ == 0) return current_cell;
|
||||
return cControl::generateId(baseId) + "-" + current_cell;
|
||||
}
|
||||
return baseId + "-" + current_cell;
|
||||
}
|
||||
|
||||
location cTilemap::getCell(const std::string& id) const {
|
||||
size_t y_pos = id.find_last_of("y");
|
||||
size_t x_pos = id.find_last_of("x");
|
||||
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);
|
||||
}
|
||||
|
||||
std::string cTilemap::buildId(const std::string& base, size_t x, size_t y) {
|
||||
std::ostringstream sout;
|
||||
if(!base.empty()) {
|
||||
sout << base << '-';
|
||||
}
|
||||
sout << 'x' << x << 'y' << y;
|
||||
return sout.str();
|
||||
}
|
||||
|
||||
bool cTilemap::hasChild(std::string id) const {
|
||||
return controls.find(id) != controls.end();
|
||||
}
|
||||
|
||||
cControl& cTilemap::getChild(std::string id) {
|
||||
if(!hasChild(id)) throw std::invalid_argument(id + " was not found in the tilemap");
|
||||
return *controls[id];
|
||||
}
|
||||
|
||||
bool cTilemap::hasChild(std::string id, size_t x, size_t y) const {
|
||||
return hasChild(buildId(id, x, y));
|
||||
}
|
||||
|
||||
cControl& cTilemap::getChild(std::string id, size_t x, size_t y) {
|
||||
return getChild(buildId(id, x, y));
|
||||
}
|
||||
|
||||
bool cTilemap::hasChild(size_t x, size_t y) const {
|
||||
return hasChild("", x, y);
|
||||
}
|
||||
|
||||
cControl& cTilemap::getChild(size_t x, size_t y) {
|
||||
return getChild("", x, y);
|
||||
}
|
||||
|
||||
bool cTilemap::manageFormat(eFormat prop, bool set, boost::any* val) {
|
||||
switch(prop) {
|
||||
case TXT_FRAME:
|
||||
if(val) {
|
||||
if(set) frameStyle = boost::any_cast<eFrameStyle>(*val);
|
||||
else *val = frameStyle;
|
||||
}
|
||||
break;
|
||||
// TODO: Colour is not supported
|
||||
default: return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cTilemap::isClickable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cTilemap::isFocusable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cTilemap::isScrollable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void cTilemap::draw() {
|
||||
if(!isVisible()) return;
|
||||
for(auto& ctrl : controls) {
|
||||
rectangle localBounds = ctrl.second->getBounds();
|
||||
rectangle globalBounds = localBounds;
|
||||
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::recalcRect() {
|
||||
auto iter = controls.begin();
|
||||
auto location = frame.topLeft();
|
||||
frame = {INT_MAX, INT_MAX, 0, 0};
|
||||
while(iter != controls.end()){
|
||||
cControl& ctrl = *iter->second;
|
||||
rectangle otherFrame = ctrl.getBounds();
|
||||
if(otherFrame.right > frame.right)
|
||||
frame.right = otherFrame.right;
|
||||
if(otherFrame.bottom > frame.bottom)
|
||||
frame.bottom = otherFrame.bottom;
|
||||
if(otherFrame.left < frame.left)
|
||||
frame.left = otherFrame.left;
|
||||
if(otherFrame.top < frame.top)
|
||||
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;
|
||||
}
|
||||
|
||||
void cTilemap::fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs) {
|
||||
for(auto p : controls) {
|
||||
cControl& ctrl = *p.second;
|
||||
if(ctrl.getType() == CTRL_FIELD) {
|
||||
cTextField& field = dynamic_cast<cTextField&>(ctrl);
|
||||
if(field.tabOrder > 0)
|
||||
specificTabs.push_back(field.tabOrder);
|
||||
else if(field.tabOrder < 0)
|
||||
reverseTabs.push_back(field.tabOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cTilemap::cTilemap(cDialog& parent) : cContainer(CTRL_MAP, parent) {}
|
||||
|
||||
void cTilemap::forEach(std::function<void(std::string,cControl&)> callback) {
|
||||
for(auto ctrl : controls)
|
||||
callback(ctrl.first, *ctrl.second);
|
||||
}
|
||||
|
||||
bool cTilemap::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
|
||||
if(attr.Name() == "rows") {
|
||||
try {
|
||||
attr.GetValue(&rows);
|
||||
} catch(ticpp::Exception&) {
|
||||
throw xBadVal(tagName, attr.Name(), attr.Value(), attr.Row(), attr.Column(), fname);
|
||||
}
|
||||
return true;
|
||||
} else if(attr.Name() == "cols") {
|
||||
try {
|
||||
attr.GetValue(&cols);
|
||||
} catch(ticpp::Exception&) {
|
||||
throw xBadVal(tagName, attr.Name(), attr.Value(), attr.Row(), attr.Column(), fname);
|
||||
}
|
||||
return true;
|
||||
} else if(attr.Name() == "cellspacing") {
|
||||
try {
|
||||
attr.GetValue(&spacing);
|
||||
} catch(ticpp::Exception&) {
|
||||
throw xBadVal(tagName, attr.Name(), attr.Value(), attr.Row(), attr.Column(), fname);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return cContainer::parseAttribute(attr, tagName, fname);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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);
|
||||
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);
|
||||
}
|
||||
54
src/dialogxml/widgets/tilemap.hpp
Normal file
54
src/dialogxml/widgets/tilemap.hpp
Normal file
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// tilemap.hpp
|
||||
// BoE
|
||||
//
|
||||
// Created by Celtic Minstrel on 2025-02-01.
|
||||
//
|
||||
|
||||
#ifndef BoE_DIALOG_TILEMAP_HPP
|
||||
#define BoE_DIALOG_TILEMAP_HPP
|
||||
|
||||
#include "container.hpp"
|
||||
|
||||
/// A tilemap defines a two-dimensional array of data using a repeated template.
|
||||
class cTilemap : public cContainer {
|
||||
std::map<std::string,cControl*> controls;
|
||||
size_t rows, cols, spacing = 0, cellWidth, cellHeight;
|
||||
bool manageFormat(eFormat prop, bool set, boost::any* val) override;
|
||||
location getCell(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;
|
||||
public:
|
||||
std::string generateId(const std::string& baseId) const override;
|
||||
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;
|
||||
bool isClickable() const override;
|
||||
bool isFocusable() const override;
|
||||
bool isScrollable() const override;
|
||||
void draw() override;
|
||||
bool hasChild(std::string id) const override;
|
||||
cControl& getChild(std::string id) override;
|
||||
bool hasChild(std::string id, size_t x, size_t y) const;
|
||||
cControl& getChild(std::string id, size_t x, size_t y);
|
||||
bool hasChild(size_t x, size_t y) const;
|
||||
cControl& getChild(size_t x, size_t y);
|
||||
/// 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.
|
||||
/// Meant for internal use.
|
||||
void fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs);
|
||||
/// Create a new tilemap
|
||||
/// @param parent The parent dialog.
|
||||
cTilemap(cDialog& parent);
|
||||
/// @copydoc cControl::getSupportedHandlers
|
||||
///
|
||||
/// @todo Document possible handlers
|
||||
std::set<eDlogEvt> getSupportedHandlers() const override {
|
||||
return {EVT_CLICK, EVT_FOCUS, EVT_DEFOCUS};
|
||||
}
|
||||
void forEach(std::function<void(std::string,cControl&)> callback) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -259,7 +259,7 @@ The `<stack>` tag
|
||||
-----------------
|
||||
|
||||
The `<stack>` tag groups elements that represent a single entry in an array.
|
||||
It can contain any elements except for nested `<stack>` or `<pane>` elements.
|
||||
It can contain any elements except for nested `<stack>`, `<pane>`, or `<tilemap>` elements.
|
||||
|
||||
The `<stack>` tag accepts the following attributes:
|
||||
|
||||
@@ -287,7 +287,7 @@ The `<pane>` tag
|
||||
----------------
|
||||
|
||||
The `<pane>` tag groups elements into a scrollable subpane.
|
||||
It can contain any elements except for nested `<stack>` or `<pane>` elements.
|
||||
It can contain any elements except for nested `<stack>`, `<pane>`, or `<tilemap>` elements.
|
||||
|
||||
The `<pane>` tag accepts the following attributes:
|
||||
|
||||
@@ -295,6 +295,20 @@ The `<pane>` tag accepts the following attributes:
|
||||
* `outline` - See **Common Attributes** above.
|
||||
* `style` - Same as for `<slider>`, see above. Applies to the pane's scrollbar.
|
||||
|
||||
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.
|
||||
|
||||
The `<tilemap>` tag accepts the following attributes:
|
||||
|
||||
* `framed` - See **Common Attributes** above. Defaults to `false`.
|
||||
* `outline` - See **Common Attributes** above.
|
||||
* `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.
|
||||
|
||||
Keyboard Shortcuts
|
||||
------------------
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "dialogxml/dialogs/dialog.hpp"
|
||||
#include "dialogxml/widgets/control.hpp"
|
||||
#include "dialogxml/widgets/button.hpp"
|
||||
#include "dialogxml/widgets/tilemap.hpp"
|
||||
#include "dialogxml/dialogs/strdlog.hpp"
|
||||
#include "dialogxml/dialogs/3choice.hpp"
|
||||
#include "dialogxml/dialogs/strchoice.hpp"
|
||||
@@ -517,10 +518,10 @@ static bool edit_ter_obj(cDialog& me, ter_num_t which_ter) {
|
||||
obj[check.obj_pos.x][check.obj_pos.y] = check.picture;
|
||||
}
|
||||
obj[me["x"].getTextAsNum()][me["y"].getTextAsNum()] = pic;
|
||||
cTilemap& map = dynamic_cast<cTilemap&>(me["map"]);
|
||||
for(int x = 0; x < 4; x++) {
|
||||
for(int y = 0; y < 4; y++) {
|
||||
std::string id = "x" + std::to_string(x) + "y" + std::to_string(y);
|
||||
dynamic_cast<cPict&>(me[id]).setPict(obj[x][y]);
|
||||
dynamic_cast<cPict&>(map.getChild(x,y)).setPict(obj[x][y]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user