Implement relative positioning system for DialogXML
This allows widgets to be positioned relative to other widgets. Two dialogs have been converted to use relative positioning. Also fix some issues with calculating the dialog rect.
This commit is contained in:
@@ -1,58 +1,58 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
|
||||
<dialog defbtn='done'>
|
||||
<button name='help' relative='neg pos' type='help' def-key='help' top='6' left='22'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='next' top='10' left='8' width='205' height='16'>Create/Edit Party:</text>
|
||||
<pict type='dlog' num='7' top='6' left='6'/>
|
||||
<text size='large' top='16' left='50' width='205' height='16'>Create/Edit Party:</text>
|
||||
<button name='help' type='help' def-key='help' top='4' left='498'/>
|
||||
|
||||
<pict name='pc1' type='pc' num='0' top='52' left='12'/>
|
||||
<text size='large' top='63' left='42' width='40' height='16'>PC #1</text>
|
||||
<text name='name1' size='title' font='dungeon' framed='true' top='60' left='93' width='152' height='20'/>
|
||||
<button name='delete1' type='tall' top='50' left='258'>Delete</button>
|
||||
<button name='trait1' type='trait' top='50' left='324'/>
|
||||
<button name='train1' type='tall' top='50' left='390'>Train</button>
|
||||
<button name='pic1' type='tall' top='50' left='455'>Graphic</button>
|
||||
<pict name='pc1' type='pc' relative='abs pos' rel-anchor='prev' num='0' top='10' left='12'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='11' left='2' width='40' height='16'>PC #1</text>
|
||||
<text name='name1' size='title' font='dungeon' framed='true' relative='pos neg' rel-anchor='prev' top='3' left='11' width='152' height='20'/>
|
||||
<button name='delete1' type='tall' relative='pos neg' rel-anchor='prev' top='10' left='13'>Delete</button>
|
||||
<button name='trait1' type='trait' relative='pos pos-in' rel-anchor='prev' top='0' left='3'/>
|
||||
<button name='train1' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Train</button>
|
||||
<button name='pic1' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Graphic</button>
|
||||
|
||||
<pict name='pc2' type='pc' num='0' top='97' left='12'/>
|
||||
<text size='large' top='108' left='42' width='40' height='16'>PC #2</text>
|
||||
<text name='name2' size='title' font='dungeon' framed='true' top='105' left='93' width='152' height='20'/>
|
||||
<button name='delete2' type='tall' top='95' left='258'>Delete</button>
|
||||
<button name='trait2' type='trait' top='95' left='324'/>
|
||||
<button name='train2' type='tall' top='95' left='390'>Train</button>
|
||||
<button name='pic2' type='tall' top='95' left='455'>Graphic</button>
|
||||
<pict name='pc2' type='pc' relative='abs pos' anchor='pc1' num='0' top='9' left='12'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='11' left='2' width='40' height='16'>PC #2</text>
|
||||
<text name='name2' size='title' font='dungeon' framed='true' relative='pos neg' rel-anchor='prev' top='3' left='11' width='152' height='20'/>
|
||||
<button name='delete2' type='tall' relative='pos neg' rel-anchor='prev' top='10' left='13'>Delete</button>
|
||||
<button name='trait2' type='trait' relative='pos pos-in' rel-anchor='prev' top='0' left='3'/>
|
||||
<button name='train2' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Train</button>
|
||||
<button name='pic2' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Graphic</button>
|
||||
|
||||
<pict name='pc3' type='pc' num='0' top='142' left='12'/>
|
||||
<text size='large' top='163' left='42' width='40' height='16'>PC #3</text>
|
||||
<text name='name3' size='title' font='dungeon' framed='true' top='150' left='93' width='152' height='20'/>
|
||||
<button name='delete3' type='tall' top='140' left='258'>Delete</button>
|
||||
<button name='trait3' type='trait' top='140' left='324'/>
|
||||
<button name='train3' type='tall' top='140' left='390'>Train</button>
|
||||
<button name='pic3' type='tall' top='140' left='455'>Graphic</button>
|
||||
<pict name='pc3' type='pc' relative='abs pos' anchor='pc2' num='0' top='9' left='12'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='11' left='2' width='40' height='16'>PC #3</text>
|
||||
<text name='name3' size='title' font='dungeon' framed='true' relative='pos neg' rel-anchor='prev' top='3' left='11' width='152' height='20'/>
|
||||
<button name='delete3' type='tall' relative='pos neg' rel-anchor='prev' top='10' left='13'>Delete</button>
|
||||
<button name='trait3' type='trait' relative='pos pos-in' rel-anchor='prev' top='0' left='3'/>
|
||||
<button name='train3' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Train</button>
|
||||
<button name='pic3' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Graphic</button>
|
||||
|
||||
<pict name='pc4' type='pc' num='0' top='187' left='12'/>
|
||||
<text size='large' top='198' left='42' width='40' height='16'>PC #4</text>
|
||||
<text name='name4' size='title' font='dungeon' framed='true' top='195' left='93' width='152' height='20'/>
|
||||
<button name='delete4' type='tall' top='185' left='258'>Delete</button>
|
||||
<button name='trait4' type='trait' top='185' left='324'/>
|
||||
<button name='train4' type='tall' top='185' left='390'>Train</button>
|
||||
<button name='pic4' type='tall' top='185' left='455'>Graphic</button>
|
||||
<pict name='pc4' type='pc' relative='abs pos' anchor='pc3' num='0' top='9' left='12'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='11' left='2' width='40' height='16'>PC #4</text>
|
||||
<text name='name4' size='title' font='dungeon' framed='true' relative='pos neg' rel-anchor='prev' top='3' left='11' width='152' height='20'/>
|
||||
<button name='delete4' type='tall' relative='pos neg' rel-anchor='prev' top='10' left='13'>Delete</button>
|
||||
<button name='trait4' type='trait' relative='pos pos-in' rel-anchor='prev' top='0' left='3'/>
|
||||
<button name='train4' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Train</button>
|
||||
<button name='pic4' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Graphic</button>
|
||||
|
||||
<pict name='pc5' type='pc' num='0' top='232' left='12'/>
|
||||
<text size='large' top='243' left='42' width='40' height='16'>PC #5</text>
|
||||
<text name='name5' size='title' font='dungeon' framed='true' top='240' left='93' width='152' height='20'/>
|
||||
<button name='delete5' type='tall' top='230' left='258'>Delete</button>
|
||||
<button name='trait5' type='trait' top='230' left='324'/>
|
||||
<button name='train5' type='tall' top='230' left='390'>Train</button>
|
||||
<button name='pic5' type='tall' top='230' left='455'>Graphic</button>
|
||||
<pict name='pc5' type='pc' relative='abs pos' anchor='pc4' num='0' top='9' left='12'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='11' left='2' width='40' height='16'>PC #5</text>
|
||||
<text name='name5' size='title' font='dungeon' framed='true' relative='pos neg' rel-anchor='prev' top='3' left='11' width='152' height='20'/>
|
||||
<button name='delete5' type='tall' relative='pos neg' rel-anchor='prev' top='10' left='13'>Delete</button>
|
||||
<button name='trait5' type='trait' relative='pos pos-in' rel-anchor='prev' top='0' left='3'/>
|
||||
<button name='train5' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Train</button>
|
||||
<button name='pic5' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Graphic</button>
|
||||
|
||||
<pict name='pc6' type='pc' num='0' top='277' left='12'/>
|
||||
<text size='large' top='288' left='42' width='40' height='16'>PC #6</text>
|
||||
<text name='name6' size='title' font='dungeon' framed='true' top='285' left='93' width='152' height='20'/>
|
||||
<button name='delete6' type='tall' top='275' left='258'>Delete</button>
|
||||
<button name='trait6' type='trait' top='275' left='324'/>
|
||||
<button name='train6' type='tall' top='275' left='390'>Train</button>
|
||||
<button name='pic6' type='tall' top='275' left='455'>Graphic</button>
|
||||
<pict name='pc6' type='pc' relative='abs pos' anchor='pc5' num='0' top='9' left='12'/>
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='11' left='2' width='40' height='16'>PC #6</text>
|
||||
<text name='name6' size='title' font='dungeon' framed='true' relative='pos neg' rel-anchor='prev' top='3' left='11' width='152' height='20'/>
|
||||
<button name='delete6' type='tall' relative='pos neg' rel-anchor='prev' top='10' left='13'>Delete</button>
|
||||
<button name='trait6' type='trait' relative='pos pos-in' rel-anchor='prev' top='0' left='3'/>
|
||||
<button name='train6' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Train</button>
|
||||
<button name='pic6' type='tall' relative='pos pos-in' rel-anchor='prev' top='0' left='3'>Graphic</button>
|
||||
|
||||
<text top='324' left='9' width='181' height='15'>Click PC to rename.</text>
|
||||
<button name='done' type='done' top='320' left='455'/>
|
||||
<text relative='abs neg' top='20' left='9' width='181' height='15'>Click PC to rename.</text>
|
||||
<button name='done' relative='pos-in pos' anchor='pic6' type='done' top='3' left='0'/>
|
||||
</dialog>
|
||||
|
||||
@@ -1,46 +1,45 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
|
||||
<?xml-stylesheet href="dialog.xsl" type="text/xsl"?>
|
||||
<dialog defbtn='okay'>
|
||||
<!--
|
||||
TODO: All the LEDs here should have bold labels according to the original DITL; do they get that by default? The XSL transformation with CSS stylesheet doesn't give them bold labels.
|
||||
-->
|
||||
<pict type='dlog' num='16' top='8' left='8'/>
|
||||
<text size='large' top='6' left='49' width='218' height='16'>
|
||||
Blades of Exile Preferences:
|
||||
<text size='large' relative='pos pos-in' rel-anchor='prev' top='0' left='4' width='218' height='16'>
|
||||
Blades of Exile Preferences
|
||||
</text>
|
||||
<text size='large' top='25' left='59' width='182' height='17'>Display alignment:</text>
|
||||
<text framed='true' top='48' left='64' width='277' height='102'/>
|
||||
<text size='large' relative='pos-in pos' rel-anchor='prev' top='3' left='10' width='182' height='17'>Display alignment:</text>
|
||||
<text name='disp-frame' framed='true' relative='pos-in pos' rel-anchor='prev' top='6' left='5' width='277' height='102'/>
|
||||
<group name='display'>
|
||||
<led name='tl' top='55' left='72' width='54'>Top Left</led>
|
||||
<led name='tr' top='55' left='213' width='62'>Top Right</led>
|
||||
<led name='mid' top='88' left='147' width='75'>Center</led>
|
||||
<led name='bl' top='126' left='72' width='104'>Bottom Left</led>
|
||||
<led name='br' top='126' left='213' width='104'>Bottom Right</led>
|
||||
<led name='win' top='159' left='66' width='228'>Small Window (not full screen)</led>
|
||||
<led name='tl' relative='pos-in' anchor='disp-frame' top='7' left='8' width='54'>Top Left</led>
|
||||
<led name='tr' relative='neg-in pos-in' anchor='disp-frame' top='7' left='128' width='62'>Top Right</led>
|
||||
<led name='mid' relative='pos-in' anchor='disp-frame' top='40' left='83' width='75'>Center</led>
|
||||
<led name='bl' relative='pos-in neg-in' anchor='disp-frame' top='24' left='8' width='104'>Bottom Left</led>
|
||||
<led name='br' relative='neg-in' anchor='disp-frame' top='24' left='128' width='104'>Bottom Right</led>
|
||||
<led name='win' relative='pos-in pos' anchor='disp-frame' top='9' left='2' width='228'>Small Window (not full screen)</led>
|
||||
</group>
|
||||
<text size='large' top='181' left='54' width='182' height='17'>Game speed:</text>
|
||||
<text name='spd-head' size='large' relative='neg pos' anchor='disp-frame' top='31' left='10' width='182' height='17'>Game speed:</text>
|
||||
<group name='speed'>
|
||||
<led name='fast' top='202' left='69' width='53'>Fast</led>
|
||||
<led name='med' top='202' left='151' width='53'>Medium</led>
|
||||
<led name='slow' top='202' left='244' width='53'>Slow</led>
|
||||
<led name='snail' top='202' left='328' width='70'>Quite Slow</led>
|
||||
<led name='fast' relative='pos-in pos' anchor='spd-head' top='4' left='15' width='53'>Fast</led>
|
||||
<led name='med' relative='pos-in pos' anchor='spd-head' top='4' left='97' width='53'>Medium</led>
|
||||
<led name='slow' relative='pos-in pos' anchor='spd-head' top='4' left='190' width='53'>Slow</led>
|
||||
<led name='snail' relative='pos-in pos' anchor='spd-head' top='4' left='274' width='70'>Quite Slow</led>
|
||||
</group>
|
||||
<text size='large' top='221' left='53' width='182' height='17'>For older machines:</text>
|
||||
<led name='nofrills' top='244' left='53' width='335'>No graphics frills (lose special effects)</led>
|
||||
<led name='noanim' top='264' left='53' width='335'>Turn off terrain animation</led>
|
||||
<led name='noshore' top='284' left='53' width='335'>Turn of frills on shore</led>
|
||||
<led name='nomaps' top='309' left='53' width='120'>Don't Save Maps</led>
|
||||
<led name='nosound' top='309' left='228' width='117'>No Sounds</led>
|
||||
<led name='repeatdesc' top='329' left='53' width='287'>Show room descriptions more than once</led>
|
||||
<led name='nohelp' top='349' left='53' width='306'>Never show instant help</led>
|
||||
<led name='resethelp' top='369' left='53' width='340'>
|
||||
<text size='large' relative='pos-in pos' anchor='spd-head' top='23' left='0' width='182' height='17'>For older machines:</text>
|
||||
<led name='nofrills' relative='pos-in pos' rel-anchor='prev' top='6' left='0' width='335'>
|
||||
No graphics frills (lose special effects)
|
||||
</led>
|
||||
<led name='noanim' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='335'>Turn off terrain animation</led>
|
||||
<led name='noshore' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='335'>Turn of frills on shore</led>
|
||||
<led name='nomaps' relative='pos-in pos' rel-anchor='prev' top='15' left='0' width='120'>Don't Save Maps</led>
|
||||
<led name='nosound' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='117'>No Sounds</led>
|
||||
<led name='repeatdesc' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='287'>Show room descriptions more than once</led>
|
||||
<led name='nohelp' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='306'>Never show instant help</led>
|
||||
<led name='resethelp' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='340'>
|
||||
Reset instant help (all help windows will reappear)
|
||||
</led>
|
||||
<led name='easier' top='389' left='53' width='352'>Make game easier (monsters much weaker)</led>
|
||||
<led name='lesswm' top='409' left='53' width='340'>Fewer wandering monsters</led>
|
||||
<led name='skipsplash' top='429' left='53' width='340'>Skip splash screen on startup</led>
|
||||
<led name='scaleui' top='449' left='53' width='340'>Apply UI scaling</led>
|
||||
<led name='easier' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='352'>Make game easier (monsters much weaker)</led>
|
||||
<led name='lesswm' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='340'>Fewer wandering monsters</led>
|
||||
<led name='skipsplash' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='340'>Skip splash screen on startup</led>
|
||||
<led name='scaleui' relative='pos-in pos' rel-anchor='prev' top='10' left='0' width='340'>Apply UI scaling</led>
|
||||
|
||||
<button name='okay' type='regular' top='466' left='354'>OK</button>
|
||||
<button name='cancel' type='regular' def-key='esc' top='466' left='281'>Cancel</button>
|
||||
<button name='okay' relative='abs pos' rel-anchor='prev' type='regular' top='7' left='354'>OK</button>
|
||||
<button name='cancel' relative='neg pos-in' anchor='okay' type='regular' def-key='esc' top='0' left='73'>Cancel</button>
|
||||
</dialog>
|
||||
|
||||
@@ -71,6 +71,25 @@
|
||||
<xs:enumeration value="double"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="relAnchor">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="next"/>
|
||||
<xs:enumeration value="prev"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
<xs:simpleType name="relPosition">
|
||||
<xs:list>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:enumeration value="abs"/>
|
||||
<xs:enumeration value="pos"/>
|
||||
<xs:enumeration value="neg"/>
|
||||
<xs:enumeration value="pos-in"/>
|
||||
<xs:enumeration value="neg-in"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:list>
|
||||
</xs:simpleType>
|
||||
<xs:attributeGroup name="rect-size">
|
||||
<xs:attribute name="top" use="required" type="xs:integer"/>
|
||||
<xs:attribute name="left" use="required" type="xs:integer"/>
|
||||
@@ -81,6 +100,11 @@
|
||||
<xs:attribute name="framed" default="false" type="bool"/>
|
||||
<xs:attribute name="outline" default="inset" type="frameStyle"/>
|
||||
</xs:attributeGroup>
|
||||
<xs:attributeGroup name="position">
|
||||
<xs:attribute name="anchor" type="xs:token"/>
|
||||
<xs:attribute name="rel-anchor" type="relAnchor"/>
|
||||
<xs:attribute name="relative" default="abs" type="relPosition"/>
|
||||
</xs:attributeGroup>
|
||||
<xs:attribute name="def-key" type="key"/>
|
||||
<xs:attributeGroup name="font">
|
||||
<xs:attribute name="font" default="bold">
|
||||
@@ -136,6 +160,7 @@
|
||||
<xs:attribute ref="def-key"/>
|
||||
<xs:attribute name="fromlist" default="none" type="xs:string"/>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="pict">
|
||||
<xs:attribute name="name" type="xs:token"/>
|
||||
@@ -157,6 +182,7 @@
|
||||
<xs:attribute ref="def-key"/>
|
||||
<xs:attribute name="num" use="required" type="xs:integer"/>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType mixed="true" name="button">
|
||||
<xs:sequence>
|
||||
@@ -170,6 +196,7 @@
|
||||
<xs:attribute ref="def-key"/>
|
||||
<xs:attribute name="fromlist" default="none" type="xs:string"/>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="led">
|
||||
<xs:simpleContent>
|
||||
@@ -185,6 +212,7 @@
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
<xs:attributeGroup ref="font"/>
|
||||
<xs:attribute name="wrap" default="false" type="bool"/>
|
||||
</xs:extension>
|
||||
@@ -199,6 +227,7 @@
|
||||
<xs:attribute name="link" type="xs:token"/>
|
||||
<xs:attribute name="style" type="scrollStyle" default="led"/>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
</xs:complexType>
|
||||
<xs:complexType name="ledGroup">
|
||||
<xs:sequence>
|
||||
@@ -236,6 +265,7 @@
|
||||
<xs:attributeGroup ref="frame"/>
|
||||
<xs:attribute name="style" type="scrollStyle" default="led"/>
|
||||
<xs:attributeGroup ref="rect-size"/>
|
||||
<xs:attributeGroup ref="position"/>
|
||||
</xs:complexType>
|
||||
<xs:element name="dialog">
|
||||
<xs:complexType>
|
||||
@@ -278,5 +308,9 @@
|
||||
<xs:selector xpath="slider"/>
|
||||
<xs:field xpath="@link"/>
|
||||
</xs:keyref>
|
||||
<xs:keyref name="anchorLink" refer="uniqueID">
|
||||
<xs:selector xpath="*"/>
|
||||
<xs:field xpath="@anchor"/>
|
||||
</xs:keyref>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
|
||||
@@ -216,44 +216,133 @@ void cDialog::loadFromFile(std::string path){
|
||||
}
|
||||
|
||||
vector<int> specificTabs, reverseTabs;
|
||||
std::pair<std::string,cControl*> prevCtrl{"", nullptr};
|
||||
for(node = node.begin(xml.FirstChildElement()); node != node.end(); node++){
|
||||
node->GetValue(&type);
|
||||
ctrlIter inserted;
|
||||
// Yes, I'm using insert instead of [] to add elements to the map.
|
||||
// In this situation, it's actually easier that way; the reason being, the
|
||||
// map key is obtained from the name attribute of each element.
|
||||
if(type == "field") {
|
||||
auto field = parse<cTextField>(*node);
|
||||
controls.insert(field);
|
||||
inserted = controls.insert(field).first;
|
||||
tabOrder.push_back(field);
|
||||
if(field.second->tabOrder > 0)
|
||||
specificTabs.push_back(field.second->tabOrder);
|
||||
else if(field.second->tabOrder < 0)
|
||||
reverseTabs.push_back(field.second->tabOrder);
|
||||
} else if(type == "text")
|
||||
controls.insert(parse<cTextMsg>(*node));
|
||||
inserted = controls.insert(parse<cTextMsg>(*node)).first;
|
||||
else if(type == "pict")
|
||||
controls.insert(parse<cPict>(*node));
|
||||
inserted = controls.insert(parse<cPict>(*node)).first;
|
||||
else if(type == "slider")
|
||||
controls.insert(parse<cScrollbar>(*node));
|
||||
inserted = controls.insert(parse<cScrollbar>(*node)).first;
|
||||
else if(type == "button")
|
||||
controls.insert(parse<cButton>(*node));
|
||||
inserted = controls.insert(parse<cButton>(*node)).first;
|
||||
else if(type == "led")
|
||||
controls.insert(parse<cLed>(*node));
|
||||
inserted = controls.insert(parse<cLed>(*node)).first;
|
||||
else if(type == "group")
|
||||
controls.insert(parse<cLedGroup>(*node));
|
||||
inserted = controls.insert(parse<cLedGroup>(*node)).first;
|
||||
else if(type == "stack") {
|
||||
auto parsed = parse<cStack>(*node);
|
||||
controls.insert(parsed);
|
||||
inserted = controls.insert(parsed).first;
|
||||
// Now, if it contains any fields, their tab order must be accounted for
|
||||
parsed.second->fillTabOrder(specificTabs, reverseTabs);
|
||||
} else if(type == "pane") {
|
||||
auto parsed = parse<cScrollPane>(*node);
|
||||
controls.insert(parsed);
|
||||
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 throw xBadNode(type,node->Row(),node->Column(),fname);
|
||||
if(prevCtrl.second) {
|
||||
if(inserted->second->anchor == "$$prev$$" && prevCtrl.second->anchor == "$$next$$") {
|
||||
throw xBadVal(type, "anchor", "<circular dependency>", node->Row(), node->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;
|
||||
}
|
||||
|
||||
// Resolve relative positioning
|
||||
bool all_resolved = true;
|
||||
do {
|
||||
all_resolved = true;
|
||||
for(auto& p : controls) {
|
||||
auto ctrl = p.second;
|
||||
if(!ctrl->anchor.empty()) {
|
||||
auto anchor = controls[ctrl->anchor];
|
||||
if(!anchor->anchor.empty()) {
|
||||
// Make sure it's not a loop!
|
||||
std::vector<std::string> refs{ctrl->anchor};
|
||||
while(!anchor->anchor.empty()) {
|
||||
refs.push_back(anchor->anchor);
|
||||
anchor = controls[anchor->anchor];
|
||||
if(std::find(refs.begin(), refs.end(), anchor->anchor) != refs.end()) {
|
||||
std::string ctrlType;
|
||||
switch(ctrl->getType()) {
|
||||
case CTRL_UNKNOWN: ctrlType = "???"; break;
|
||||
case CTRL_BTN: ctrlType = "button"; break;
|
||||
case CTRL_LED: ctrlType = "led"; break;
|
||||
case CTRL_PICT: ctrlType = "pict"; break;
|
||||
case CTRL_FIELD: ctrlType = "field"; break;
|
||||
case CTRL_TEXT: ctrlType = "text"; break;
|
||||
case CTRL_GROUP: ctrlType = "group"; break;
|
||||
case CTRL_STACK: ctrlType = "stack"; break;
|
||||
case CTRL_SCROLL: ctrlType = "slider"; break;
|
||||
case CTRL_PANE: ctrlType = "pane"; break;
|
||||
}
|
||||
throw xBadVal(ctrlType, "anchor", "<circular dependency>", 0, 0, fname);
|
||||
}
|
||||
}
|
||||
all_resolved = false;
|
||||
continue;
|
||||
}
|
||||
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([this, &all_resolved](const std::string&, cControl& ctrl) {
|
||||
// TODO: Deduplicate this code (it's functionally identical to the above non-container code)
|
||||
if(!ctrl.anchor.empty()) {
|
||||
auto anchor = controls[ctrl.anchor];
|
||||
if(!anchor->anchor.empty()) {
|
||||
// Make sure it's not a loop!
|
||||
std::vector<std::string> refs{ctrl.anchor};
|
||||
while(!anchor->anchor.empty()) {
|
||||
refs.push_back(anchor->anchor);
|
||||
anchor = controls[anchor->anchor];
|
||||
if(std::find(refs.begin(), refs.end(), anchor->anchor) != refs.end()) {
|
||||
std::string ctrlType;
|
||||
switch(ctrl.getType()) {
|
||||
case CTRL_UNKNOWN: ctrlType = "???"; break;
|
||||
case CTRL_BTN: ctrlType = "button"; break;
|
||||
case CTRL_LED: ctrlType = "led"; break;
|
||||
case CTRL_PICT: ctrlType = "pict"; break;
|
||||
case CTRL_FIELD: ctrlType = "field"; break;
|
||||
case CTRL_TEXT: ctrlType = "text"; break;
|
||||
case CTRL_GROUP: ctrlType = "group"; break;
|
||||
case CTRL_STACK: ctrlType = "stack"; break;
|
||||
case CTRL_SCROLL: ctrlType = "slider"; break;
|
||||
case CTRL_PANE: ctrlType = "pane"; break;
|
||||
}
|
||||
throw xBadVal(ctrlType, "anchor", "<circular dependency>", 0, 0, fname);
|
||||
}
|
||||
}
|
||||
all_resolved = false;
|
||||
return;
|
||||
}
|
||||
ctrl.relocateRelative(ctrl.frame.topLeft(), anchor, ctrl.horz, ctrl.vert);
|
||||
ctrl.anchor.clear();
|
||||
ctrl.horz = ctrl.vert = POS_ABS;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} while(!all_resolved);
|
||||
|
||||
// Set the default button.
|
||||
if(hasControl(defaultButton))
|
||||
getControl(defaultButton).attachKey(enterKey);
|
||||
@@ -317,29 +406,43 @@ void cDialog::loadFromFile(std::string path){
|
||||
// now calculate window rect
|
||||
winRect = rectangle();
|
||||
recalcRect();
|
||||
ctrlIter iter = controls.begin();
|
||||
currentFocus = "";
|
||||
while(iter != controls.end()){
|
||||
for(ctrlIter iter = controls.begin(); iter != controls.end(); iter++){
|
||||
if(typeid(iter->second) == typeid(cTextField*)){
|
||||
if(currentFocus.empty()) currentFocus = iter->first;
|
||||
break;
|
||||
}
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void cDialog::recalcRect(){
|
||||
ctrlIter iter = controls.begin();
|
||||
while(iter != controls.end()){
|
||||
bool haveRel = false;
|
||||
for(ctrlIter iter = controls.begin(); iter != controls.end(); iter++) {
|
||||
using namespace std::placeholders;
|
||||
if(auto container = dynamic_cast<cContainer*>(iter->second))
|
||||
container->forEach(std::bind(&cControl::recalcRect, _2));
|
||||
iter->second->recalcRect();
|
||||
rectangle frame = iter->second->getBounds();
|
||||
if(frame.right > winRect.right)
|
||||
haveRel = haveRel || iter->second->horz != POS_ABS || iter->second->vert != POS_ABS;
|
||||
if(iter->second->horz != POS_REL_NEG && frame.right > winRect.right)
|
||||
winRect.right = frame.right;
|
||||
if(frame.bottom > winRect.bottom)
|
||||
if(iter->second->vert != POS_REL_NEG && frame.bottom > winRect.bottom)
|
||||
winRect.bottom = frame.bottom;
|
||||
iter++;
|
||||
}
|
||||
winRect.right += 6;
|
||||
winRect.bottom += 6;
|
||||
if(!haveRel) return;
|
||||
// Resolve any remaining relative positions
|
||||
// Controls placed relative to the dialog's edges can go off the edge of the dialog
|
||||
for(ctrlIter iter = controls.begin(); iter != controls.end(); iter++) {
|
||||
location pos = iter->second->getBounds().topLeft();
|
||||
if(iter->second->horz == POS_REL_NEG)
|
||||
pos.x = winRect.right - pos.x;
|
||||
if(iter->second->vert == POS_REL_NEG)
|
||||
pos.y = winRect.bottom - pos.y;
|
||||
iter->second->horz = iter->second->vert = POS_ABS;
|
||||
iter->second->relocate(pos);
|
||||
}
|
||||
}
|
||||
|
||||
void cDialog::init(){
|
||||
|
||||
@@ -161,10 +161,11 @@ bool cButton::parseContent(ticpp::Node& content, int n, std::string tagName, std
|
||||
return cControl::parseContent(content, n, tagName, fname, text);
|
||||
}
|
||||
|
||||
static const std::set<eBtnType> labelledButtons{BTN_TINY, BTN_LED, BTN_PUSH};
|
||||
|
||||
void cButton::validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& elems) {
|
||||
cControl::validatePostParse(elem, fname, attrs, elems);
|
||||
if(getType() == CTRL_BTN && !attrs.count("type")) throw xMissingAttr(elem.Value(), "type", elem.Row(), elem.Column(), fname);
|
||||
static const std::set<eBtnType> labelledButtons{BTN_TINY, BTN_LED, BTN_PUSH};
|
||||
if(labelledButtons.count(type)) {
|
||||
if(!attrs.count("color") && !attrs.count("colour") && parent->getBg() == cDialog::BG_DARK)
|
||||
setColour(sf::Color::White);
|
||||
@@ -177,6 +178,17 @@ location cButton::getPreferredSize() {
|
||||
return {btnRects[type][0].width(), btnRects[type][0].height()};
|
||||
}
|
||||
|
||||
void cButton::recalcRect() {
|
||||
location bestSz = getPreferredSize();
|
||||
if(labelledButtons.count(type)) {
|
||||
if(frame.width() < bestSz.x) frame.width() = bestSz.x;
|
||||
if(frame.height() < bestSz.y) frame.height() = bestSz.y;
|
||||
} else {
|
||||
frame.width() = bestSz.x;
|
||||
frame.height() = bestSz.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Indices within the buttons array.
|
||||
size_t cButton::btnGW[14] = {
|
||||
0, // BTN_SM
|
||||
@@ -404,6 +416,10 @@ bool cLed::parseContent(ticpp::Node& content, int n, std::string tagName, std::s
|
||||
return cButton::parseContent(content, n, tagName, fname, text);
|
||||
}
|
||||
|
||||
location cLed::getPreferredSize() {
|
||||
return {ledRects[0][0].width(), ledRects[0][0].height()};
|
||||
}
|
||||
|
||||
void cLedGroup::addChoice(cLed* ctrl, std::string key) {
|
||||
choices[key] = ctrl;
|
||||
if(ctrl->getState() != led_off)
|
||||
@@ -649,6 +665,5 @@ bool cLedGroup::parseContent(ticpp::Node& content, int n, std::string tagName, s
|
||||
void cLedGroup::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
|
||||
// Don't defer to super-class; groups are an abstract container that doesn't require a position.
|
||||
//cControl::validatePostParse(who, fname, attrs, nodes);
|
||||
recalcRect();
|
||||
frameStyle = FRM_NONE;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ public:
|
||||
bool parseContent(ticpp::Node& content, int n, std::string tagName, std::string fname, std::string& text) override;
|
||||
void validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& elems) override;
|
||||
location getPreferredSize() override;
|
||||
void recalcRect() override;
|
||||
/// Set the type of this button.
|
||||
/// @param newType The desired button type.
|
||||
void setBtnType(eBtnType newType);
|
||||
@@ -115,6 +116,7 @@ public:
|
||||
static bool noAction(cDialog&,std::string,eKeyMod) {return true;}
|
||||
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;
|
||||
location getPreferredSize() override;
|
||||
storage_t store() override;
|
||||
void restore(storage_t to) override;
|
||||
/// Create a new LED button.
|
||||
@@ -239,7 +241,7 @@ public:
|
||||
/// Recalculate the LED group's bounding rect.
|
||||
/// 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 recalcRect() override;
|
||||
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;
|
||||
|
||||
@@ -37,6 +37,29 @@ void cControl::relocate(location to) {
|
||||
frame.offset(to.x - frame.left, to.y - frame.top);
|
||||
}
|
||||
|
||||
void cControl::relocateRelative(location to, cControl* anchor, ePosition h, ePosition v) {
|
||||
if(anchor == nullptr) anchor = this;
|
||||
// Determine the anchor point of the relocation
|
||||
location anchorPoint;
|
||||
switch(h) {
|
||||
case POS_ABS: anchorPoint.x = 0; break;
|
||||
case POS_REL_PLUS: anchorPoint.x = anchor->frame.right; break;
|
||||
case POS_REL_NEG: anchorPoint.x = anchor->frame.left; to.x = -to.x; break;
|
||||
case POS_CONT_PLUS: anchorPoint.x = anchor->frame.left; break;
|
||||
case POS_CONT_NEG: anchorPoint.x = anchor->frame.right; to.x = -to.x; break;
|
||||
}
|
||||
switch(v) {
|
||||
case POS_ABS: anchorPoint.y = 0; break;
|
||||
case POS_REL_PLUS: anchorPoint.y = anchor->frame.bottom; break;
|
||||
case POS_REL_NEG: anchorPoint.y = anchor->frame.top; to.y = -to.y; break;
|
||||
case POS_CONT_PLUS: anchorPoint.y = anchor->frame.top; break;
|
||||
case POS_CONT_NEG: anchorPoint.y = anchor->frame.bottom; to.y = -to.y; break;
|
||||
}
|
||||
to.x += anchorPoint.x;
|
||||
to.y += anchorPoint.y;
|
||||
relocate(to);
|
||||
}
|
||||
|
||||
const char* xHandlerNotSupported::msg[4] = {
|
||||
"This control cannot handle click events.\n",
|
||||
"This control cannot handle focus events.\n",
|
||||
@@ -491,6 +514,51 @@ std::string cControl::parse(ticpp::Element& who, std::string fname) {
|
||||
bool cControl::parseAttribute(ticpp::Attribute& attr, std::string tagName, std::string fname) {
|
||||
std::string name;
|
||||
attr.GetName(&name);
|
||||
// Relative positioning
|
||||
if(name == "relative") {
|
||||
static auto space = " \t";
|
||||
std::string rel = attr.Value();
|
||||
const xBadVal err(tagName, name, rel, attr.Row(), attr.Column(), fname);
|
||||
size_t border = rel.find_first_of(space);
|
||||
if(border != std::string::npos) {
|
||||
size_t border_end = rel.find_last_of(space);
|
||||
// Error if any of [border, border_end] are not spaces
|
||||
for(size_t i = border + 1; i < border_end; i++) {
|
||||
if(rel[i] != ' ' && rel[i] != '\t') throw err;
|
||||
}
|
||||
std::string h = rel.substr(0, border), v = rel.substr(border_end + 1);
|
||||
if(h == "abs") horz = POS_ABS;
|
||||
else if(h == "pos") horz = POS_REL_PLUS;
|
||||
else if(h == "neg") horz = POS_REL_NEG;
|
||||
else if(h == "pos-in") horz = POS_CONT_PLUS;
|
||||
else if(h == "neg-in") horz = POS_CONT_NEG;
|
||||
else throw err;
|
||||
if(v == "abs") vert = POS_ABS;
|
||||
else if(v == "pos") vert = POS_REL_PLUS;
|
||||
else if(v == "neg") vert = POS_REL_NEG;
|
||||
else if(v == "pos-in") vert = POS_CONT_PLUS;
|
||||
else if(v == "neg-in") vert = POS_CONT_NEG;
|
||||
else throw err;
|
||||
}
|
||||
else if(rel == "abs") horz = vert = POS_ABS;
|
||||
else if(rel == "pos") horz = vert = POS_REL_PLUS;
|
||||
else if(rel == "neg") horz = vert = POS_REL_NEG;
|
||||
else if(rel == "pos-in") horz = vert = POS_CONT_PLUS;
|
||||
else if(rel == "neg-in") horz = vert = POS_CONT_NEG;
|
||||
else throw err;
|
||||
return true;
|
||||
}
|
||||
if(name == "anchor") {
|
||||
anchor = attr.Value();
|
||||
return true;
|
||||
}
|
||||
if(name == "rel-anchor") {
|
||||
std::string val = attr.Value();
|
||||
if(val == "next") anchor = "$$next$$";
|
||||
else if(val == "prev") anchor = "$$prev$$";
|
||||
else throw xBadVal(tagName, name, val, attr.Row(), attr.Column(), fname);
|
||||
return true;
|
||||
}
|
||||
// Colour and formatting, if supported
|
||||
if(name == "framed" && canFormat(TXT_FRAME)) {
|
||||
std::string val;
|
||||
@@ -559,6 +627,13 @@ bool cControl::parseContent(ticpp::Node&, int, std::string, std::string, std::st
|
||||
void cControl::validatePostParse(ticpp::Element& elem, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>&) {
|
||||
if(!attrs.count("left")) throw xMissingAttr(elem.Value(), "left", elem.Row(), elem.Column(), fname);
|
||||
if(!attrs.count("top")) throw xMissingAttr(elem.Value(), "top", elem.Row(), elem.Column(), fname);
|
||||
if(attrs.count("relative") && !attrs.count("anchor") && !attrs.count("rel-anchor")) {
|
||||
// If relative is specified, an anchor is required... unless it's abs or neg
|
||||
if((horz != POS_ABS && horz != POS_REL_NEG) || (vert != POS_ABS && vert != POS_REL_NEG))
|
||||
throw xMissingAttr(elem.Value(), "anchor", elem.Row(), elem.Column(), fname);
|
||||
}
|
||||
if(attrs.count("anchor") && attrs.count("rel-anchor"))
|
||||
throw xBadAttr(elem.Value(), "(rel-)anchor", elem.Row(), elem.Column(), fname);
|
||||
}
|
||||
|
||||
cControl::~cControl() {}
|
||||
|
||||
@@ -55,6 +55,14 @@ enum eControlType {
|
||||
CTRL_PANE, ///< A scroll pane
|
||||
};
|
||||
|
||||
enum ePosition {
|
||||
POS_ABS, ///< Absolute positioning (possibly relative to a container)
|
||||
POS_REL_PLUS, ///< Positioned relative to another widget, measuring down from its bottom edge or right from its right edge
|
||||
POS_REL_NEG, ///< Positioned relative to another widget, measuring up from its top edge or left from its left edge
|
||||
POS_CONT_PLUS, ///< Positioned relative to another widget, measuring down from its top edge or right from its left edge
|
||||
POS_CONT_NEG, ///< Positioned relative to another widget, measuering up from its bottom edge or left from its right edge
|
||||
};
|
||||
|
||||
/// Thrown when you try to set a handler that the control does not support.
|
||||
class xHandlerNotSupported : public std::exception {
|
||||
static const char* msg[4];
|
||||
@@ -251,6 +259,13 @@ public:
|
||||
/// Set the position of this control.
|
||||
/// @param to The new position.
|
||||
void relocate(location to);
|
||||
/// Set the position of this control relative to another control.
|
||||
/// @param to The new relative position.
|
||||
/// @param anchor The position will be calculated relative to this control's position.
|
||||
/// If nullptr, the control will be moved relative to its current position.
|
||||
/// @param horz How to place the control on the horizontal axis.
|
||||
/// @param vert How to place the control on the vertical axis.
|
||||
void relocateRelative(location to, cControl* anchor, ePosition horz, ePosition vert);
|
||||
/// Get the control's text as an integer.
|
||||
/// @return The control's text, coerced to an integer.
|
||||
long long getTextAsNum();
|
||||
@@ -330,6 +345,10 @@ public:
|
||||
cControl& operator=(cControl& other) = delete;
|
||||
cControl(cControl& other) = delete;
|
||||
protected:
|
||||
/// If the control automatically determines its rect based on certain criteria, override this.
|
||||
/// It will automatically be called during parsing.
|
||||
/// When overridden, it should normally be public.
|
||||
virtual void recalcRect() {}
|
||||
/// Returns a list of event handlers that this control supports.
|
||||
/// @return The list of handlers as a std::set.
|
||||
///
|
||||
@@ -426,6 +445,9 @@ private:
|
||||
friend class cDialog; // TODO: This is only so it can access parseColour... hack!
|
||||
eControlType type;
|
||||
std::map<eDlogEvt, boost::any> event_handlers;
|
||||
// Transient values only used during parsing
|
||||
ePosition horz = POS_ABS, vert = POS_ABS;
|
||||
std::string anchor;
|
||||
};
|
||||
|
||||
/// A superclass to represent a control that contains other controls.
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
/// @param num The new icon index.
|
||||
void setPict(pic_num_t num);
|
||||
/// Automatically recalculate the icon's bounding rect based on its current picture.
|
||||
void recalcRect();
|
||||
void recalcRect() override;
|
||||
/// Get the current icon.
|
||||
/// @return The number of the current icon.
|
||||
pic_num_t getPicNum();
|
||||
|
||||
@@ -219,5 +219,4 @@ bool cScrollPane::parseContent(ticpp::Node& content, int n, std::string tagName,
|
||||
void cScrollPane::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
|
||||
cContainer::validatePostParse(who, fname, attrs, nodes);
|
||||
if(!attrs.count("style")) setStyle(SCROLL_LED);
|
||||
recalcRect();
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
/// @note This function is intended for internal use, which is why it takes a control pointer instead of a unique key.
|
||||
void addChild(cControl* ctrl, std::string key);
|
||||
/// Recalculate the pane's bounding rect based on its contained controls.
|
||||
void recalcRect();
|
||||
void recalcRect() override;
|
||||
/// Get the pane's current scroll position.
|
||||
/// @return The current position.
|
||||
long getPosition();
|
||||
|
||||
@@ -201,6 +201,5 @@ bool cStack::parseContent(ticpp::Node& content, int n, std::string tagName, std:
|
||||
|
||||
void cStack::validatePostParse(ticpp::Element& who, std::string fname, const std::set<std::string>& attrs, const std::multiset<std::string>& nodes) {
|
||||
validatePostParse(who, fname, attrs, nodes);
|
||||
recalcRect();
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
/// @return The number of pages
|
||||
size_t getPageCount();
|
||||
/// Recalculate the stack's bounding rect based on its contained controls.
|
||||
void recalcRect();
|
||||
void recalcRect() override;
|
||||
/// Adds any fields in this stack to the tab order building arrays.
|
||||
/// Meant for internal use.
|
||||
void fillTabOrder(std::vector<int>& specificTabs, std::vector<int>& reverseTabs);
|
||||
|
||||
@@ -27,7 +27,25 @@ The following attributes are allowed on all or most elements:
|
||||
rect of the control within the dialog. All non-container controls
|
||||
support these attributes, and in fact the `top` and `left` attributes
|
||||
are required. Some controls may ignore the `width` and `height`
|
||||
attributes.
|
||||
attributes. All of these must be non-negative integers.
|
||||
* `relative` - Specifies how the location is computed; defaults to `"abs"`.
|
||||
Must be one or two (space-separated) of the following;
|
||||
if two are specified, they represent the mode used for calculating x and y respectively:
|
||||
* `abs` - Computed in global dialog space, relative to the top left corner.
|
||||
* `pos` - Computed relative to the reference widget's bottom right corner.
|
||||
* `pos-in` - Computed relative to the reference widget's top left corner.
|
||||
* `neg` - Computed relative to the reference widget's top left corner,
|
||||
with the axes inverted (as if top and left were negative).
|
||||
As a special case, if this is used with no reference widget,
|
||||
the widget's size does not contribute to computation of the dialog's size,
|
||||
and the widget is positioned relative to the dialog's bottom right corner.
|
||||
* `neg-in` - Computed relative to the reference widget's bottom right corner,
|
||||
with the axes inverted.
|
||||
* `anchor` - Specifies the `name` of the reference widget for this widget's location.
|
||||
* `rel-anchor` - Set to `prev` or `next` to use the previous or next element in the XML ordering
|
||||
as the reference widget for this widget's location.
|
||||
This currently does not work for widgets in containers.
|
||||
Mutually exclusive with `anchor`.
|
||||
* `def-key` - Specifies the default keyboard shortcut for the
|
||||
control. See **Keyboard Shortcuts** below for more information on the
|
||||
format of this attribute.
|
||||
|
||||
Reference in New Issue
Block a user