Changeset 80:ea58f277f487

Show
Ignore:
Timestamp:
08/07/08 06:25:27 (5 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
Children:

81:d8fccaa45d5f 83:2813ac68576f

branch:
default
Message:

Gui reorganization and changes; partial implementation of floating widgets.

Moved contents of mde/gui/WidgetData.d elsewhere; new gui/WidgetDataSet.d and gui/types.d modules.
Changes to widget/createWidget.d
Partially implemented FloatingAreaWidget? to provide an area for floating "window" widgets.
New DebugWidget? and some uses of it (e.g. bad widget data).
Decoupled OptionChanges? from Options.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • codeDoc/jobs.txt

    r79 r80  
    99To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): 
    1010Also 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). 
     115   setting widgets' default size? setMinSize/setDefaultSize fct? 
    12124   Try to correlate names of option sections more. (i.e. symbol name, class name, name of i18n translation file) 
    13134   Not guaranteed to catch up-click ending callback! Appears not to be a problem... 
  • data/L10n/OptionsMisc.mtt

    r43 r80  
    11{MT01} 
    22{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  
    33<char[]|Design="Working"> 
    44{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"]}> 
    1113{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  
    2626 * changes and previous changes saved to the use file, before saving to the user file. 
    2727 *************************************************************************************************/ 
    28 module mde.gui.WidgetData
     28module mde.gui.WidgetDataSet
    2929 
    30 import mde.gui.exception; 
    31 import mde.gui.widget.Ifaces; 
    32 import mde.gui.widget.createWidget; 
     30public import mde.gui.types; 
    3331 
    3432// For loading from file: 
     
    3634import mt = mde.mergetag.DefaultData; 
    3735import mt = mde.mergetag.exception; 
    38 import mde.mergetag.Reader; 
    39 import mde.mergetag.Writer; 
    40 import mde.setup.paths; 
    4136import mde.mergetag.serialize; 
    42  
    43 import tango.core.sync.Mutex; 
    4437import tango.util.log.Log : Log, Logger; 
    4538 
    4639private Logger logger; 
    4740static this () { 
    48     logger = Log.getLogger ("mde.gui.WidgetData"); 
     41    logger = Log.getLogger ("mde.gui.WidgetDataSet"); 
    4942} 
    5043 
    51  
    52 /************************************************************************************************* 
    53  * Contains the code for loading and saving the gui, but not the code for drawing it or handling 
    54  * user input. 
    55  *  
    56  * This abstract class exists solely for separating out some of the functionality. 
    57  *************************************************************************************************/ 
    58 abstract scope class WidgetLoader : IWidgetManager 
    59 { 
    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 sections 
    77     */ 
    78     private void loadData (bool allDesigns = false) { 
    79         if (allLoaded || (defaultDesign !is null && allDesigns == false)) 
    80             return; // test if already loaded 
    81          
    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 reader 
    88         scope IReader reader; 
    89         try { 
    90             reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); 
    91              
    92             // Read from the HEADER: 
    93             // Get the renderer 
    94             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             else 
    100                 rendName = *p; 
    101              
    102             // Get which section to use 
    103             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             else 
    109                 defaultDesign = *p; 
    110              
    111             // Read the body: 
    112             // Load the chosen design 
    113             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             } else 
    126                 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 lock 
    144          
    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         } else 
    153             loadData (true); 
    154          
    155          
    156         // Get data: 
    157         curData = data[name]; // NOTE: may throw 
    158          
    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 changes 
    186             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 again 
    208         } 
    209          
    210         try {   // Save 
    211             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 renderers 
    258      
    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 data 
    262     WidgetDataSet curData;      // Current data 
    263     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: 
    27444/************************************************************************************************* 
    27545 * Contains data for all widgets in a GUI. 
     
    28151        // Priority is HIGH_LOW. Only load tag if it doesn't already exist. 
    28252        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); 
    29354        } 
    29455    } 
     
    29859     
    29960    /** 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; 
    30268    } 
    30369     
     
    32490    // other entries are read from files. 
    32591    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)); 
    33394    } 
    33495    //END Mergetag code 
  • mde/gui/WidgetManager.d

    r76 r80  
    2222module mde.gui.WidgetManager; 
    2323 
    24 public import mde.gui.WidgetData
     24import mde.gui.WidgetDataSet
    2525import mde.gui.widget.Ifaces; 
    2626import mde.gui.renderer.createRenderer; 
     
    182182    wdim w,h;       // area available to the widgets 
    183183} 
     184 
     185 
     186import mde.gui.exception; 
     187import mde.gui.widget.Ifaces; 
     188import mde.gui.widget.createWidget; 
     189 
     190import mde.mergetag.Reader; 
     191import mde.mergetag.Writer; 
     192import 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*************************************************************************************************/ 
     200abstract 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  
    1717module mde.gui.renderer.IRenderer; 
    1818 
    19 // Put here to avoid circular import. 
    20 typedef int wdim; 
     19public import mde.gui.types; 
    2120 
    2221/** Interface for renderers. 
     
    7271     
    7372    //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     
    7482    /** Draw a window border plus background. */ 
    7583    void drawWindow (wdim x, wdim y, wdim w, wdim h); 
  • mde/gui/renderer/SimpleRenderer.d

    r58 r80  
    8080     
    8181     
     82    //FIXME - make these do something 
     83    void restrict (wdim x, wdim y, wdim w, wdim h) {} 
     84    void relax () {} 
     85     
    8286    void drawWindow (wdim x, wdim y, wdim w, wdim h) { 
    8387        gl.setColor (0f, 0f, .7f); 
  • mde/gui/widget/Floating.d

    r75 r80  
    1414along with this program.  If not, see <http://www.gnu.org/licenses/>. */ 
    1515 
    16 /** The Window class. Hopefully eventually this will become a widget to make things a bit more 
    17 * 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. */ 
     17module mde.gui.widget.Floating; 
     18 
     19import mde.gui.widget.Widget; 
     20import mde.gui.exception
     21/+import mde.gui.widget.createWidget; 
    2222 
    2323import mde.gui.IGui; 
    24 import mde.gui.exception; 
    2524import mde.gui.renderer.createRenderer; 
    2625 
     
    2827import mde.mergetag.parse.parseTo : parseTo; 
    2928import mde.mergetag.parse.parseFrom : parseFrom; 
    30  
     29+/ 
    3130import tango.util.log.Log : Log, Logger; 
    3231 
     
    3534    logger = Log.getLogger ("mde.gui.widget.Window"); 
    3635} 
     36//FIXME - documentation 
    3737 
    3838/** GUI Window class 
     
    4444 * created, be given its int[] of data, which this() must confirm is valid (or throw). 
    4545 */ 
    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 */ 
     56class FloatingAreaWidget : SizableWidget, IParentWidget 
    4757{ 
    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     
     99protected: 
     100    IChildWidget[] subWidgets;  // all subwidgets, framed or not 
     101    wdimPair[] sWCoords;        // coords for subwidgets, relative to this widget 
     102     
     103    /+ 
    53104    /** Call after loading is finished to setup the window and confirm that it's valid. 
    54105     * 
     
    358409     
    359410    BorderDimensions border;        // Total border size (move plus resize) 
     411    +/ 
    360412} 
  • mde/gui/widget/Ifaces.d

    r78 r80  
    2424module mde.gui.widget.Ifaces; 
    2525 
     26public import mde.gui.types; 
    2627public 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 should 
    37  * 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 anything 
    45  * else), or size. Their use instead of wdim is optional (and in some cases wdim values aren't of 
    46  * any of these types). Also don't use these aliases for variables which may also be used to other 
    47  * effects, e.g. if they can have special values with special meanings. */ 
    48 alias wdim  wdabs; 
    49 alias wdim  wdrel;  /// ditto 
    50 alias wdim  wdsize; /// ditto 
    51  
    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;  /// data 
    58 } 
    5928 
    6029 
     
    8352    /** Create a widget by ID. 
    8453     * 
     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     * 
    8558     * Creates a widget, using the widget data with index id. Widget data is loaded from files, 
    8659     * and per design (multiple gui layouts, called designs, may exist; data is per design). 
    8760     *  
    88      * Note: this method is only for "named" widgets; generic widgets instanciated in lists are 
    89      * 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); 
    9164     
    9265    /** Record some changes, for saving. */ 
     
    267240    void draw (); 
    268241} 
    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 of 
    275  * WidgetDataSet and WidgetDataChanges. 
    276  *************************************************************************************************/ 
    277 struct WidgetData 
    278 { 
    279     int[]    ints; 
    280     char[][] strings; 
    281 } 
  • mde/gui/widget/Widget.d

    r78 r80  
    2424 
    2525public import mde.gui.widget.Ifaces; 
    26 import mde.gui.renderer.IRenderer; 
    2726import mde.gui.exception; 
    2827 
  • mde/gui/widget/createWidget.d

    r77 r80  
    2424import mde.gui.widget.miscWidgets; 
    2525import mde.gui.widget.TextWidget; 
     26import mde.gui.widget.Floating; 
     27import tango.util.log.Log : Log, Logger; 
    2628 
    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) 
     29private Logger logger; 
     30static 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 *************************************************************************************************/ 
     45IChildWidget createWidget (IWidgetManager mgr, WidgetData data, IParentWidget parent) 
    3046in { 
    3147    assert (mgr !is null, "createWidget: mgr is null"); 
    3248} 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    } 
    3453    int type = data.ints[0];    // type is first element of data 
    3554     
     55    //pragma (msg, binarySearch ("type", WIDGETS)); 
    3656    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); 
    3861} 
    3962 
     
    5174/// Widget types. 
    5275enum 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 
    5778     
    5879    // Use widget names rather than usual capitals convention 
     
    6182    // blank: 0x1 
    6283    FixedBlank              = 0x1, 
    63     SizableBlank            = WSIZABLE | HSIZABLE | 0x1, 
     84    SizableBlank            = 0x2, 
     85    Debug                   = 0xF, 
    6486     
    6587    // buttons: 0x10 
    66     Button                  = INTERACTIBLE | 0x10, 
     88    Button                  = 0x10, 
    6789     
    6890    // content: 0x20 
     
    7092    Int             = 0x22, 
    7193