Changeset 80:ea58f277f487
- Timestamp:
- 08/07/08 06:25:27 (5 months ago)
- Children:
- Files:
-
- codeDoc/jobs.txt (modified) (1 diff)
- codeDoc/mergetag/new-models.vym (added)
- data/L10n/OptionsMisc.mtt (modified) (1 diff)
- data/conf/gui.mtt (modified) (1 diff)
- mde/gui/WidgetDataSet.d (moved) (moved from mde/gui/WidgetData.d) (5 diffs)
- mde/gui/WidgetManager.d (modified) (2 diffs)
- mde/gui/renderer/IRenderer.d (modified) (2 diffs)
- mde/gui/renderer/SimpleRenderer.d (modified) (1 diff)
- mde/gui/types.d (added)
- mde/gui/widget/Floating.d (moved) (moved from mde/gui/widget/Window.d) (5 diffs)
- mde/gui/widget/Ifaces.d (modified) (3 diffs)
- mde/gui/widget/Widget.d (modified) (1 diff)
- mde/gui/widget/createWidget.d (modified) (6 diffs)
- mde/gui/widget/miscWidgets.d (modified) (1 diff)
- mde/lookup/Options.d (modified) (2 diffs)
- mde/mergetag/Reader.d (modified) (1 diff)
- mde/setup/sdl.d (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
codeDoc/jobs.txt
r79 r80 9 9 To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): 10 10 Also see todo.txt and FIXME/NOTE comment marks. 11 4 struct (via tuple) support for parseTo/From. Requires rewriting with static if (is T : type). 11 5 setting widgets' default size? setMinSize/setDefaultSize fct? 12 12 4 Try to correlate names of option sections more. (i.e. symbol name, class name, name of i18n translation file) 13 13 4 Not guaranteed to catch up-click ending callback! Appears not to be a problem... data/L10n/OptionsMisc.mtt
r43 r80 1 1 {MT01} 2 2 {en-GB} 3 <entry|useThreads= ["Use threads","Global option for threading in mde."]>4 <entry|logLevel= ["Logging level","Controls which messages are logged, from 0=trace to 6=none (default: 1=info)."]>5 <entry|L10n= ["Localisation","Specifies the language to use."]>6 <entry|pollInterval= ["Polling interval","Delay in main loop to limit CPU usage"]>7 <entry|exitImmediately= ["Exit immediately","Load files and exit immediately, without running main loop (for debugging)"]>3 <entry|useThreads={0:"Use threads",1:"Global option for threading in mde."}> 4 <entry|logLevel={0:"Logging level",1:"Controls which messages are logged, from 0=trace to 6=none (default: 1=info)."}> 5 <entry|L10n={0:"Localisation",1:"Specifies the language to use."}> 6 <entry|pollInterval={0:"Polling interval",1:"Delay in main loop to limit CPU usage"}> 7 <entry|exitImmediately={0:"Exit immediately",1:"Load files and exit immediately, without running main loop (for debugging)"}> data/conf/gui.mtt
r78 r80 3 3 <char[]|Design="Working"> 4 4 {Working} 5 <WidgetData|root=[0xB004,3,3],["square","blank","square","blank","content","blank","square","blank","square"]> 6 <WidgetData|square=[0x1,6,6],[]> 7 <WidgetData|content=[0xB004,4,2],["blank","button","blank","blank","blank","opts","blank","blank"]> 8 <WidgetData|button=[0x4010,200,200],[]> 9 <WidgetData|blank=[0x3001],[]> 10 <WidgetData|opts=[0xB005, 0xfe8c00],[]> 5 <WidgetData|root={0:[0x8100,3,3],1:["square","blank","square","blank","content","blank","square","blank","square"]}> 6 <WidgetData|square={0:[0x1,6,6]}> 7 <WidgetData|content={0:[0x8100,4,2],1:["floating","button","blank","blank","blank","opts","blank","blank"]}> 8 <WidgetData|button={0:[0x10,200,200]}> 9 <WidgetData|blank={0:[0x2]}> 10 <WidgetData|opts={0:[0x8110, 0xfe8c00]}> 11 <WidgetData|floating={0:[0x8200,20,20],1:["text"]}> 12 <WidgetData|text={0:[0x21,0xFF0000],1:["Floating text"]}> 11 13 {Basic} 12 <WidgetData|root=[0x21,0x90D970],["A string!"]> 13 !{ 14 {W1} 15 <int|x=30> 16 <int|y=80> 17 <int[][int]|widgetData=[0:[0xB004,2,1,1,2],1:[0x4010,200,200],2:[0x1,100,100]]> 18 {W2} 19 <int|x=150> 20 <int|y=200> 21 <int[][int]|widgetData=[0:[0xB004,5,5,3,1,22,1,2,1,1,1,1,1,22,1,22,1,22,1,1,1,1,1,2,1,22,1,3],1:[0x3001],2:[0x21,0,0xFFFF00],3:[0x21,1,0xBFFF00],22:[0x22,1,0xFF00]]> 22 <char[][int]|widgetStrings=[0:"alpha=α", 1:"beta=β"]> 23 {WEmbedded} 24 <int|x=20> 25 <int|y=100> 26 <int[][int]|widgetData=[0:[0xB005,0,0xB04000]]> 27 } 14 <WidgetData|root={0:[0x21,0x90D970],1:["A string!"]}> mde/gui/WidgetDataSet.d
r79 r80 26 26 * changes and previous changes saved to the use file, before saving to the user file. 27 27 *************************************************************************************************/ 28 module mde.gui.WidgetData ;28 module mde.gui.WidgetDataSet; 29 29 30 import mde.gui.exception; 31 import mde.gui.widget.Ifaces; 32 import mde.gui.widget.createWidget; 30 public import mde.gui.types; 33 31 34 32 // For loading from file: … … 36 34 import mt = mde.mergetag.DefaultData; 37 35 import mt = mde.mergetag.exception; 38 import mde.mergetag.Reader;39 import mde.mergetag.Writer;40 import mde.setup.paths;41 36 import mde.mergetag.serialize; 42 43 import tango.core.sync.Mutex;44 37 import tango.util.log.Log : Log, Logger; 45 38 46 39 private Logger logger; 47 40 static this () { 48 logger = Log.getLogger ("mde.gui.WidgetData ");41 logger = Log.getLogger ("mde.gui.WidgetDataSet"); 49 42 } 50 43 51 52 /*************************************************************************************************53 * Contains the code for loading and saving the gui, but not the code for drawing it or handling54 * user input.55 *56 * This abstract class exists solely for separating out some of the functionality.57 *************************************************************************************************/58 abstract scope class WidgetLoader : IWidgetManager59 {60 /** Construct a new widget loader.61 *62 * params:63 * fileName = Name of file specifying the gui, excluding path and extension.64 */65 protected this (char[] file) {66 mutex = new Mutex; // Used on functions intended to be called from outside the gui package.67 fileName = file;68 }69 ~this () {70 save;71 }72 73 /* Load the widgets' data from the file specified to the CTOR.74 *75 * params:76 * allDesigns = Load all sections77 */78 private void loadData (bool allDesigns = false) {79 if (allLoaded || (defaultDesign !is null && allDesigns == false))80 return; // test if already loaded81 82 if (!confDir.exists (fileName)) {83 logger.error ("Unable to load GUI: no config file!");84 return; // not a fatal error (so long as the game can run without a GUI!)85 }86 87 // Set up a reader88 scope IReader reader;89 try {90 reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true);91 92 // Read from the HEADER:93 // Get the renderer94 char[]* p = "Renderer" in reader.dataset.header._charA;95 if (p is null || *p is null) {96 logger.warn ("No renderer specified: using \"Simple\"");97 rendName = "Simple";98 }99 else100 rendName = *p;101 102 // Get which section to use103 p = "Design" in reader.dataset.header._charA;104 if (p is null || *p is null) {105 logger.warn ("No gui design specified: trying \"Default\"");106 defaultDesign = "Default";107 }108 else109 defaultDesign = *p;110 111 // Read the body:112 // Load the chosen design113 reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) {114 WidgetDataSet* p = id in data;115 if (p is null) {116 data[id] = new WidgetDataSet;117 return *(id in data);118 }119 return *p;120 };121 122 if (allDesigns) {123 reader.read;124 allLoaded = true;125 } else126 reader.read([defaultDesign]);127 } catch (Exception e) {128 logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):");129 logger.error (e.msg);130 throw new GuiException ("Failure parsing config file");131 }132 }133 134 /** Load the gui from some design.135 *136 * If a design was previously loaded, its changes are saved first.137 *138 * Params:139 * name = Design to load. If null, the default will be loaded.140 */141 void loadDesign (char[] name = null) {142 if (changes !is null)143 save; // own lock144 145 mutex.lock;146 scope(exit) mutex.unlock;147 148 // Load data (loadData tests if it's already loaded first):149 if (name is null) {150 loadData (false);151 name = defaultDesign;152 } else153 loadData (true);154 155 156 // Get data:157 curData = data[name]; // NOTE: may throw158 159 // Get/create a changes section:160 if (changesDS is null)161 changesDS = new mt.DataSet;162 163 mt.IDataSection* p = name in changesDS.sec;164 if (p && ((changes = cast(WidgetDataChanges) *p) !is null)) {}165 else {166 changes = new WidgetDataChanges (curData);167 changesDS.sec[name] = changes;168 }169 170 // Create the widgets:171 createRootWidget;172 }173 174 /** Save changes, if any exist.175 *176 * Is run when the manager is destroyed, but could be run at other times too. */177 void save () {178 mutex.lock;179 scope(exit) mutex.unlock;180 181 // Make all widgets save any changed data; return if no changes:182 if (!child.saveChanges ("root"))183 return;184 185 if (loadUserFile) { // merge entries from user file into current changes186 try {187 scope IReader reader = confDir.makeMTReader (188 fileName, PRIORITY.HIGH_ONLY, changesDS, true);189 190 // Create if necessary, only corresponding to existing designs read:191 reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) {192 WidgetDataSet* p = id in data;193 if (p is null)194 throw new Exception ("File has changed since it was loaded!");195 return new WidgetDataChanges (*p);196 };197 198 reader.read;199 } catch (NoFileException) {200 // No user file exists; not an error.201 } catch (Exception e) {202 logger.error ("Error reading "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" prior to saving:");203 logger.error (e.msg);204 logger.error ("Overwriting the file.");205 // Continue...206 }207 loadUserFile = false; // don't need to do it again208 }209 210 try { // Save211 IWriter writer;212 writer = confDir.makeMTWriter (fileName, changesDS);213 writer.write;214 } catch (Exception e) {215 logger.error ("Saving to "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" failed:");216 logger.error (e.msg);217 // No point in throwing since it doesn't affect anything else.218 }219 }220 221 /** Get the names of all designs available. */222 char[][] designs() {223 synchronized(mutex) {224 loadData (true);225 return data.keys;226 }227 }228 229 230 /** Create a widget by ID. */231 IChildWidget makeWidget (widgetID id) {232 return createWidget (this, curData[id]);233 }234 235 /** For making changes. */236 void setData (widgetID id, WidgetData d) {237 changes[id] = d; // also updates WidgetDataSet in data.238 }239 240 /** Second stage of loading the widgets.241 *242 * loadDesign handles the data; this method needs to:243 * ---244 * // 1. Create the root widget:245 * child = makeWidget ("root");246 * // 2. Set the setSize, e.g.:247 * child.setWidth (child.minWidth, 1);248 * child.setHeight (child.minHeight, 1);249 * ---250 */251 // FIXME: abstract?252 void createRootWidget();253 254 protected:255 final char[] fileName;256 char[] defaultDesign; // The design specified in the file header.257 char[] rendName; // Name of renderer; for saving and creating renderers258 259 // Loaded data, indexed by design name. May not be loaded for all gui designs:260 scope WidgetDataSet[char[]] data;261 private bool allLoaded = false; // applies to data262 WidgetDataSet curData; // Current data263 WidgetDataChanges changes; // Changes for the current design.264 scope mt.DataSet changesDS; // changes and sections from user file (used for saving)265 bool loadUserFile = true; // still need to load user file for saving?266 267 scope IChildWidget child; // The primary widget.268 269 Mutex mutex; // lock on methods for use outside the package.270 }271 272 273 package:274 44 /************************************************************************************************* 275 45 * Contains data for all widgets in a GUI. … … 281 51 // Priority is HIGH_LOW. Only load tag if it doesn't already exist. 282 52 if (tp == "WidgetData" && (id in widgetData) is null) { 283 // Note: is a very simple form of struct deserialization 284 WidgetData data; 285 with(data) { 286 char[][] strs = split (dt); 287 if (strs.length != 2) 288 throw new ParseException ("Not two components"); 289 ints = parseTo!(int[]) (strs[0]); 290 strings = parseTo!(char[][]) (strs[1]); 291 } 292 widgetData[id] = data; 53 widgetData[id] = deserialize!(WidgetData) (dt); 293 54 } 294 55 } … … 298 59 299 60 /** Get the widget data for widget i. */ 300 WidgetData opIndex (widgetID i) { 301 return widgetData[i]; 61 WidgetData opIndex (widgetID id) { 62 auto p = id in widgetData; 63 if (p is null) { 64 logger.error ("No data for widget "~id~"; creating a debug widget instead."); 65 return WidgetData.dbg; 66 } 67 return *p; 302 68 } 303 69 … … 324 90 // other entries are read from files. 325 91 void writeAll (ItemDelg dlg) { 326 foreach (id,data; widgetData) { 327 // Note: is a very simple form of struct serialization 328 with(data) { 329 dlg ("WidgetData", id, 330 parseFrom!(int[]) (ints) ~ ',' ~ parseFrom!(char[][]) (strings) ); 331 } 332 } 92 foreach (id,data; widgetData) 93 dlg ("WidgetData", id, serialize!(WidgetData) (data)); 333 94 } 334 95 //END Mergetag code mde/gui/WidgetManager.d
r76 r80 22 22 module mde.gui.WidgetManager; 23 23 24 public import mde.gui.WidgetData;24 import mde.gui.WidgetDataSet; 25 25 import mde.gui.widget.Ifaces; 26 26 import mde.gui.renderer.createRenderer; … … 182 182 wdim w,h; // area available to the widgets 183 183 } 184 185 186 import mde.gui.exception; 187 import mde.gui.widget.Ifaces; 188 import mde.gui.widget.createWidget; 189 190 import mde.mergetag.Reader; 191 import mde.mergetag.Writer; 192 import mde.setup.paths; 193 194 /************************************************************************************************* 195 * Contains the code for loading and saving the gui, but not the code for drawing it or handling 196 * user input. 197 * 198 * This abstract class exists solely for separating out some of the functionality. 199 *************************************************************************************************/ 200 abstract scope class WidgetLoader : IWidgetManager 201 { 202 /** Construct a new widget loader. 203 * 204 * params: 205 * fileName = Name of file specifying the gui, excluding path and extension. 206 */ 207 protected this (char[] file) { 208 mutex = new Mutex; // Used on functions intended to be called from outside the gui package. 209 fileName = file; 210 } 211 ~this () { 212 save; 213 } 214 215 /* Load the widgets' data from the file specified to the CTOR. 216 * 217 * params: 218 * allDesigns = Load all sections 219 */ 220 private void loadData (bool allDesigns = false) { 221 if (allLoaded || (defaultDesign !is null && allDesigns == false)) 222 return; // test if already loaded 223 224 if (!confDir.exists (fileName)) { 225 logger.error ("Unable to load GUI: no config file!"); 226 return; // not a fatal error (so long as the game can run without a GUI!) 227 } 228 229 // Set up a reader 230 scope IReader reader; 231 try { 232 reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); 233 234 // Read from the HEADER: 235 // Get the renderer 236 char[]* p = "Renderer" in reader.dataset.header._charA; 237 if (p is null || *p is null) { 238 logger.warn ("No renderer specified: using \"Simple\""); 239 rendName = "Simple"; 240 } 241 else 242 rendName = *p; 243 244 // Get which section to use 245 p = "Design" in reader.dataset.header._charA; 246 if (p is null || *p is null) { 247 logger.warn ("No gui design specified: trying \"Default\""); 248 defaultDesign = "Default"; 249 } 250 else 251 defaultDesign = *p; 252 253 // Read the body: 254 // Load the chosen design 255 reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 256 WidgetDataSet* p = id in data; 257 if (p is null) { 258 data[id] = new WidgetDataSet; 259 return *(id in data); 260 } 261 return *p; 262 }; 263 264 if (allDesigns) { 265 reader.read; 266 allLoaded = true; 267 } else 268 reader.read([defaultDesign]); 269 } catch (Exception e) { 270 logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):"); 271 logger.error (e.msg); 272 throw new GuiException ("Failure parsing config file"); 273 } 274 } 275 276 /** Load the gui from some design. 277 * 278 * If a design was previously loaded, its changes are saved first. 279 * 280 * Params: 281 * name = Design to load. If null, the default will be loaded. 282 */ 283 void loadDesign (char[] name = null) { 284 if (changes !is null) 285 save; // own lock 286 287 mutex.lock; 288 scope(exit) mutex.unlock; 289 290 // Load data (loadData tests if it's already loaded first): 291 if (name is null) { 292 loadData (false); 293 name = defaultDesign; 294 } else 295 loadData (true); 296 297 298 // Get data: 299 auto p = name in data; 300 while (p is null) { 301 if (name == defaultDesign) 302 throw new GuiException ("Unable to load [specified or] default design"); 303 name = defaultDesign; // try again with the default 304 p = name in data; 305 } 306 curData = *p; 307 308 // Get/create a changes section: 309 if (changesDS is null) 310 changesDS = new mt.DataSet; 311 312 mt.IDataSection* q = name in changesDS.sec; 313 if (q && ((changes = cast(WidgetDataChanges) *q) !is null)) {} 314 else { 315 changes = new WidgetDataChanges (curData); 316 changesDS.sec[name] = changes; 317 } 318 319 // Create the widgets: 320 createRootWidget; 321 } 322 323 /** Save changes, if any exist. 324 * 325 * Is run when the manager is destroyed, but could be run at other times too. */ 326 void save () { 327 mutex.lock; 328 scope(exit) mutex.unlock; 329 330 // Make all widgets save any changed data; return if no changes: 331 if (!child.saveChanges ("root")) 332 return; 333 334 if (loadUserFile) { // merge entries from user file into current changes 335 try { 336 scope IReader reader = confDir.makeMTReader ( 337 fileName, PRIORITY.HIGH_ONLY, changesDS, true); 338 339 // Create if necessary, only corresponding to existing designs read: 340 reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 341 WidgetDataSet* p = id in data; 342 if (p is null) 343 throw new Exception ("File has changed since it was loaded!"); 344 return new WidgetDataChanges (*p); 345 }; 346 347 reader.read; 348 } catch (NoFileException) { 349 // No user file exists; not an error. 350 } catch (Exception e) { 351 logger.error ("Error reading "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" prior to saving:"); 352 logger.error (e.msg); 353 logger.error ("Overwriting the file."); 354 // Continue... 355 } 356 loadUserFile = false; // don't need to do it again 357 } 358 359 try { // Save 360 IWriter writer; 361 writer = confDir.makeMTWriter (fileName, changesDS); 362 writer.write; 363 } catch (Exception e) { 364 logger.error ("Saving to "~confDir.getFileName(fileName,PRIORITY.HIGH_ONLY)~" failed:"); 365 logger.error (e.msg); 366 // No point in throwing since it doesn't affect anything else. 367 } 368 } 369 370 /** Get the names of all designs available. */ 371 char[][] designs() { 372 synchronized(mutex) { 373 loadData (true); 374 return data.keys; 375 } 376 } 377 378 379 /** Create a widget by ID. */ 380 IChildWidget makeWidget (widgetID id, IParentWidget parent = null) { 381 debug logger.trace ("Creating widget \""~id~'"'); 382 return createWidget (this, curData[id], parent); 383 } 384 385 /** For making changes. */ 386 void setData (widgetID id, WidgetData d) { 387 changes[id] = d; // also updates WidgetDataSet in data. 388 } 389 390 /** Second stage of loading the widgets. 391 * 392 * loadDesign handles the data; this method needs to: 393 * --- 394 * // 1. Create the root widget: 395 * child = makeWidget ("root"); 396 * // 2. Set the setSize, e.g.: 397 * child.setWidth (child.minWidth, 1); 398 * child.setHeight (child.minHeight, 1); 399 * --- 400 */ 401 void createRootWidget(); 402 403 protected: 404 final char[] fileName; 405 char[] defaultDesign; // The design specified in the file header. 406 char[] rendName; // Name of renderer; for saving and creating renderers 407 408 // Loaded data, indexed by design name. May not be loaded for all gui designs: 409 scope WidgetDataSet[char[]] data; 410 private bool allLoaded = false; // applies to data 411 WidgetDataSet curData; // Current data 412 WidgetDataChanges changes; // Changes for the current design. 413 scope mt.DataSet changesDS; // changes and sections from user file (used for saving) 414 bool loadUserFile = true; // still need to load user file for saving? 415 416 scope IChildWidget child; // The primary widget. 417 418 Mutex mutex; // lock on methods for use outside the package. 419 } mde/gui/renderer/IRenderer.d
r75 r80 17 17 module mde.gui.renderer.IRenderer; 18 18 19 // Put here to avoid circular import. 20 typedef int wdim; 19 public import mde.gui.types; 21 20 22 21 /** Interface for renderers. … … 72 71 73 72 //BEGIN draw routines 73 /** Restrict following draw operations to given box. 74 * 75 * Restrict "pushes" a restriction onto a stack; relax must be called afterwards to "pop" the 76 * restriction. */ 77 void restrict (wdim x, wdim y, wdim w, wdim h); 78 79 /** See restrict. */ 80 void relax (); 81 74 82 /** Draw a window border plus background. */ 75 83 void drawWindow (wdim x, wdim y, wdim w, wdim h); mde/gui/renderer/SimpleRenderer.d
r58 r80 80 80 81 81 82 //FIXME - make these do something 83 void restrict (wdim x, wdim y, wdim w, wdim h) {} 84 void relax () {} 85 82 86 void drawWindow (wdim x, wdim y, wdim w, wdim h) { 83 87 gl.setColor (0f, 0f, .7f); mde/gui/widget/Floating.d
r75 r80 14 14 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 15 15 16 /** The Window class. Hopefully eventually this will become a widget to make things a bit more17 * generic. */ 18 module mde.gui.widget.Window; 19 20 import mde.gui. widget.Ifaces;21 import mde.gui.widget.createWidget;16 /** The Window class. Becoming a widget. */ 17 module mde.gui.widget.Floating; 18 19 import mde.gui.widget.Widget; 20 import mde.gui.exception; 21 /+import mde.gui.widget.createWidget; 22 22 23 23 import mde.gui.IGui; 24 import mde.gui.exception;25 24 import mde.gui.renderer.createRenderer; 26 25 … … 28 27 import mde.mergetag.parse.parseTo : parseTo; 29 28 import mde.mergetag.parse.parseFrom : parseFrom; 30 29 +/ 31 30 import tango.util.log.Log : Log, Logger; 32 31 … … 35 34 logger = Log.getLogger ("mde.gui.widget.Window"); 36 35 } 36 //FIXME - documentation 37 37 38 38 /** GUI Window class … … 44 44 * created, be given its int[] of data, which this() must confirm is valid (or throw). 45 45 */ 46 class Window : mt.IDataSection, IWindow 46 /** An area to contain floating widgets. 47 * 48 * The position of each sub-widget is set from data, but not the size. 49 * Rationale: parents' need to set subwidgets' positions when its position is set, so it needs to 50 * know their positions. Size setting is still under work FIXME. 51 * 52 * Data: Each string item is interpreted as a subwidget widgetID. 53 * Ints supplied may consist of just the widget type or 54 * additionally an (x,y) coordinate for each subwidget (all x coords first, then all y coords). 55 */ 56 class FloatingAreaWidget : SizableWidget, IParentWidget 47 57 { 48 //BEGIN Methods for GUI 49 this (char[] id) { 50 name = id; 51 } 52 58 this (IWidgetManager mgr, WidgetData data) { 59 subWidgets.length = data.strings.length; 60 foreach (i,s; data.strings) 61 subWidgets[i] = mgr.makeWidget (s, this); 62 sWCoords.length = subWidgets.length; 63 64 if (data.ints.length != 1) { 65 if (data.ints.length != 2*subWidgets.length + 1) { 66 throw new WidgetDataException; 67 } 68 foreach (i, ref c; sWCoords) { 69 c.x = cast(wdim) data.ints[i + 1]; 70 c.y = cast(wdim) data.ints[i + 1 + sWCoords.length]; 71 } 72 } 73 74 super (mgr, data); 75 76 foreach (w; subWidgets) { 77 //FIXME: set default size 78 w.setWidth (w.minWidth, -1); 79 w.setHeight (w.minHeight, -1); 80 } 81 } 82 83 void setPosition (wdim x, wdim y) { 84 super.setPosition (x,y); 85 86 foreach (i,c; sWCoords) 87 subWidgets[i].setPosition (x+c.x, y+c.y); 88 } 89 90 void draw () { 91 super.draw; 92 93 mgr.renderer.restrict (x,y, w,h); 94 95 foreach (w; subWidgets) 96 w.draw; 97 } 98 99 protected: 100 IChildWidget[] subWidgets; // all subwidgets, framed or not 101 wdimPair[] sWCoords; // coords for subwidgets, relative to this widget 102 103 /+ 53 104 /** Call after loading is finished to setup the window and confirm that it's valid. 54 105 * … … 358 409 359 410 BorderDimensions border; // Total border size (move plus resize) 411 +/ 360 412 } mde/gui/widget/Ifaces.d
r78 r80 24 24 module mde.gui.widget.Ifaces; 25 25 26 public import mde.gui.types; 26 27 public import mde.gui.renderer.IRenderer; 27 28 29 /** Widget ID type. Each ID is unique under this window.30 *31 * Type is int since this is the widget data type. */32 alias char[] widgetID;33 34 /** Window coordinate and dimension/size type (int).35 *36 * Used to disambiguate between general integers and coordinates; all widget positions/sizes should37 * use this type (or one of the aliases below).38 *39 * ---40 * typedef int wdim; // Declared in IRenderer to avoid a circular import.41 * ---42 *43 * Aliases of wdim providing extra information about what their contents hold: absolute position,44 * position relative to the containing widget (wdrel should not be used if relative to anything45 * else), or size. Their use instead of wdim is optional (and in some cases wdim values aren't of46 * any of these types). Also don't use these aliases for variables which may also be used to other47 * effects, e.g. if they can have special values with special meanings. */48 alias wdim wdabs;49 alias wdim wdrel; /// ditto50 alias wdim wdsize; /// ditto51 52 /** A pair of wdim variables, and strictly no other data (methods may be added if deemed useful).53 *54 * Potentially usable to return two wdim variables, e.g. width and height, from a function.55 * However, the current usage of out variables looks like it's better. */56 struct wdimPair {57 wdim x, y; /// data58 }59 28 60 29 … … 83 52 /** Create a widget by ID. 84 53 * 54 * Params: 55 * id = Identifier, within data files, of the data for the widget. 56 * parent = Reference to the widget's parent, passed if the widget supports recieving it. 57 * 85 58 * Creates a widget, using the widget data with index id. Widget data is loaded from files, 86 59 * and per design (multiple gui layouts, called designs, may exist; data is per design). 87 60 * 88 * Note: this method is only for "named" widgets; generic widgets instanciated in lists are89 * created differently. */90 IChildWidget makeWidget (widgetID id );61 * Note: this method is only for widgets with an identifier; generic widgets instanciated in 62 * lists are created directly and have no WidgetData of their own. */ 63 IChildWidget makeWidget (widgetID id, IParentWidget parent = null); 91 64 92 65 /** Record some changes, for saving. */ … … 267 240 void draw (); 268 241 } 269 270 271 /*************************************************************************************************272 * The data type all widgets creatable by the widget manager receive on creation.273 *274 * Conversion code to/from MT tags is contained in the addTag and writeAll methods of275 * WidgetDataSet and WidgetDataChanges.276 *************************************************************************************************/277 struct WidgetData278 {279 int[] ints;280 char[][] strings;281 }mde/gui/widget/Widget.d
r78 r80 24 24 25 25 public import mde.gui.widget.Ifaces; 26 import mde.gui.renderer.IRenderer;27 26 import mde.gui.exception; 28 27 mde/gui/widget/createWidget.d
r77 r80 24 24 import mde.gui.widget.miscWidgets; 25 25 import mde.gui.widget.TextWidget; 26 import mde.gui.widget.Floating; 27 import tango.util.log.Log : Log, Logger; 26 28 27 /** Create a widget of type data[0] (see enum WIDGET_TYPES) under manager mgr, with initialisation 28 * data [1..$]. */ 29 IChildWidget createWidget (IWidgetManager mgr, WidgetData data) 29 private Logger logger; 30 static this () { 31 logger = Log.getLogger ("mde.gui.widget.createWidget"); 32 } 33 34 /** Create a widget. 35 * 36 * Usually called by the widget manager's makeWidget function. 37 * 38 * Widget created of type data.ints[0] (see enum WIDGET_TYPES), with one of the following CTORs: 39 * --- 40 * this (IWidgetManager mgr, WidgetData data); 41 * // Called if (data.ints[0] & WIDGET_TYPES.TAKES_PARENT): 42 * this (IWidgetManager mgr, WidgetData data, IParentWidget parent); 43 * --- 44 *************************************************************************************************/ 45 IChildWidget createWidget (IWidgetManager mgr, WidgetData data, IParentWidget parent) 30 46 in { 31 47 assert (mgr !is null, "createWidget: mgr is null"); 32 48 } body { 33 if (data.ints.length < 1) throw new WidgetDataException ("No widget data"); 49 if (data.ints.length < 1) { 50 logger.error ("No int data; creating a debug widget"); 51 data.ints = [WIDGET_TYPE.Debug]; 52 } 34 53 int type = data.ints[0]; // type is first element of data 35 54 55 //pragma (msg, binarySearch ("type", WIDGETS)); 36 56 mixin (binarySearch ("type", WIDGETS)); // creates widget by type as new *Widget (mgr, data); 37 throw new WidgetDataException ("Bad widget type"); 57 58 // Not returned a new widget... 59 logger.error ("Bad widget type: {}; creating a debug widget instead.",type); 60 return new DebugWidget (mgr, data); 38 61 } 39 62 … … 51 74 /// Widget types. 52 75 enum WIDGET_TYPE : int { 53 WSIZABLE = 0x1000, // horizontally resizable 54 HSIZABLE = 0x2000, // vertically resizable 55 INTERACTIBLE = 0x4000, // any specific interaction 56 LAYOUT = 0x8000, // is a layout widget (i.e. has sub-widgets)? 76 TAKES_PARENT = 0x4000, // CTOR takes reference to its parent 77 PARENT = 0x8000, // widget can have children 57 78 58 79 // Use widget names rather than usual capitals convention … … 61 82 // blank: 0x1 62 83 FixedBlank = 0x1, 63 SizableBlank = WSIZABLE | HSIZABLE | 0x1, 84 SizableBlank = 0x2, 85 Debug = 0xF, 64 86 65 87 // buttons: 0x10 66 Button = INTERACTIBLE |0x10,88 Button = 0x10, 67 89 68 90 // content: 0x20 … … 70 92 Int = 0x22, 71 93
