Changeset 75:25cb7420dc91

Show
Ignore:
Timestamp:
07/28/08 13:17:48 (6 months ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
Message:

A massive overhaul/rewrite for the gui's data management and setup code. Currently much that was working is broken.

imde's classes are created in a static this instead of mde's main.
gl setup code moved from gl/basic.d to gl/draw.d
mergetag.DefaultData?: now HIGH_LOW priority instead of LOW_HIGH. Reduced type list to only used types; small fix for indent function.
setup.paths: new NoFileException? thrown instead of MTFileIOException

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • codeDoc/gui/GUI notes.txt

    r60 r75  
    66 
    77GUI: 
    8 ->  Basic OpenGL code to: 
    9     ->* create orthographic projection 
    10     ->* draw boxes 
    11     ->  maybe more (text, textures, ...) 
    128->  Windows with size & position 
    139    ->  position from Gui 
     
    1612        ->  no ability to resize yet except from config files 
    1713    ->  scripted widgets 
    18 ->  Text rendering 
    19     -> text library? 
    2014->  Drag & drop 
    2115    ->  click/drag start triggers a callback on the widget 
  • codeDoc/jobs.txt

    r74 r75  
    1010To do (importance 0-5: 0 pointless, 1 no obvious impact now, 2 todo sometime, 3 useful, 4 important, 5 urgent): 
    1111Also see todo.txt and FIXME/NOTE comment marks. 
     124   struct (via tuple) support for parseTo/From. Requires rewriting with static if (is T : type). 
    12134   Try to correlate names of option sections more. (i.e. symbol name, class name, name of i18n translation file) 
    13144   Not guaranteed to catch up-click ending callback! Appears not to be a problem... 
  • codeDoc/todo.txt

    r74 r75  
    33 
    44 
    5 * means done: 
    6  
    75GUI: 
    86->  Widgets: 
    97    ->  rethink how widgets are created and receive creation data, so that they don't have to be created by the Window 
    10     ->* minimum size but expandable, auto-set 
    11     ->* grid "layout" widgets 
    128    ->  scripted widgets 
    139    ->  decent rendering/theme system 
     
    2420->  possibilities 
    2521    ->  per-widget merging (i.e. separate tag(s) for each widget's data)? 
    26         ->  if a widget with sub-widgets is defined in a base file, but redesigned in a derived file, any unused widgets with data resolting are not created 
     22        ->  if a widget with sub-widgets is defined in a base file, but redesigned in a derived file, any unused widgets with data resulting are not created 
    2723        ->  if design changes in a base file, while the old design was modified in a derived file, will the result be sane? 
    2824        ->  if a locally modified gui is updated upstream (so the base files change), should: 
  • data/conf/gui.mtt

    r72 r75  
    11{MT01} 
    2 !<char[]|Renderer="Simple"> 
     2<char[]|Renderer="Simple"> 
     3<char[]|Design="Basic"> 
     4{Basic} 
     5<WidgetData|root=[0x4010,200,200],""> 
     6!{ 
    37{W1} 
    48<int|x=30> 
     
    1418<int|y=100> 
    1519<int[][int]|widgetData=[0:[0xB005,0,0xB04000]]> 
     20} 
  • mde/gl/basic.d

    r57 r75  
    1414along with this program.  If not, see <http://www.gnu.org/licenses/>. */ 
    1515 
    16 /** Some basic OpenGL code for setting up a projection and drawing. 
     16/** Some basic OpenGL code for drawing. 
    1717* 
    1818* Everything here is really intended as makeshift code to enable GUI development. */ 
     
    2222 
    2323import tango.time.Time;     // TimeSpan (type only; unused) 
    24  
    25 //BEGIN GL & window setup 
    26 void glSetup () { 
    27     glDisable(GL_LIGHTING); 
    28     glDisable(GL_DEPTH_TEST); 
    29     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
    30     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
    31     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
    32     glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
    33     glEnable(GL_TEXTURE_2D); 
    34     glShadeModel(GL_SMOOTH); 
    35      
    36     glClearColor (0.0f, 0.0f, 0.0f, 0.0f); 
    37      
    38     glMatrixMode(GL_MODELVIEW); 
    39     glLoadIdentity(); 
    40      
    41     // Used for font rendering: 
    42     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
    43     glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
    44     //NOTE: wrap mode may have an effect, but shouldn't be noticed... 
    45 } 
    46  
    47 void setProjection (int w, int h) { 
    48     glMatrixMode (GL_PROJECTION); 
    49     glLoadIdentity (); 
    50      
    51     glViewport (0,0,w,h); 
    52      
    53     // Make the top-left the origin (see gui/GUI notes.txt): 
    54     // Note that this only affects vertex operations − direct rasterisation operations are 
    55     // unaffected! 
    56     glOrtho (0.0,w, h,0.0, -1.0, 1.0); 
    57      
    58     glMatrixMode(GL_MODELVIEW); 
    59 } 
    60 //END GL & window setup 
    6124 
    6225//BEGIN Drawing utils 
  • mde/gl/draw.d

    r67 r75  
    1414along with this program.  If not, see <http://www.gnu.org/licenses/>. */ 
    1515 
    16 /** The OpenGL draw loop
     16/** The OpenGL draw loop and some basic OpenGL code to set up a projection
    1717* 
    1818* Everything here is really intended as makeshift code to enable GUI development. */ 
    1919module mde.gl.draw; 
    2020 
    21 import mde.gui.Gui; 
     21import mde.gui.WidgetManager; 
     22import mde.imde; 
    2223 
    2324import derelict.sdl.sdl; 
     
    3233    logger = Log.getLogger ("mde.gl.draw"); 
    3334} 
     35 
     36//BEGIN GL & window setup 
     37void glSetup () { 
     38    glDisable(GL_LIGHTING); 
     39    glDisable(GL_DEPTH_TEST); 
     40    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
     41    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
     42    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
     43    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
     44    glEnable(GL_TEXTURE_2D); 
     45    glShadeModel(GL_SMOOTH); 
     46     
     47    glClearColor (0.0f, 0.0f, 0.0f, 0.0f); 
     48     
     49    glMatrixMode(GL_MODELVIEW); 
     50    glLoadIdentity(); 
     51     
     52    // Used for font rendering: 
     53    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
     54    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
     55    //NOTE: wrap mode may have an effect, but shouldn't be noticed... 
     56} 
     57 
     58void setProjection (int w, int h) { 
     59    glMatrixMode (GL_PROJECTION); 
     60    glLoadIdentity (); 
     61     
     62    glViewport (0,0,w,h); 
     63     
     64    // Make the top-left the origin (see gui/GUI notes.txt): 
     65    // Note that this only affects vertex operations − direct rasterisation operations are 
     66    // unaffected! 
     67    glOrtho (0.0,w, h,0.0, -1.0, 1.0); 
     68     
     69    glMatrixMode(GL_MODELVIEW); 
     70     
     71    // The gui is tied to this viewport. 
     72    gui.setSize (w,h); 
     73} 
     74//END GL & window setup 
    3475 
    3576//BEGIN Drawing loop 
  • mde/gui/WidgetManager.d

    r74 r75  
    1414along with this program.  If not, see <http://www.gnu.org/licenses/>. */ 
    1515 
    16 /** The Gui class. 
    17 
    18 * This is the module to use externally to create a graphical user interface (likely also with 
    19 * content modules). 
    20 
    21 * Possibly add a GuiManager to update all active GUIs and pass coordinates (remapping if necessary). */ 
    22 module mde.gui.Gui
     16/************************************************************************************************* 
     17 * The gui manager class. 
     18 * 
     19 * This is the module to use externally to create a graphical user interface (likely also with 
     20 * content modules). 
     21 *************************************************************************************************/ 
     22module mde.gui.WidgetManager
    2323 
    24 import mde.gui.IGui
     24public import mde.gui.WidgetData
    2525import mde.gui.widget.Ifaces; 
    26 import mde.gui.widget.Window; 
    27 import mde.gui.exception; 
     26import mde.gui.renderer.createRenderer; 
    2827 
    2928// For adding the input event callbacks and requesting redraws: 
     
    3231import mde.scheduler.Scheduler; 
    3332 
    34 // For loading from file: 
    35 import mt = mde.mergetag.DataSet; 
    36 import mt = mde.mergetag.DefaultData; 
    37 import mt = mde.mergetag.exception; 
    38 import mde.mergetag.Reader; 
    39 import mde.mergetag.Writer; 
    40 import mde.setup.paths; 
    41  
     33import tango.core.sync.Mutex; 
    4234import tango.util.log.Log : Log, Logger; 
    4335 
    4436private Logger logger; 
    4537static this () { 
    46     logger = Log.getLogger ("mde.gui.gui"); 
    47      
    48     gui = new Gui;  // until Guis are handled otherwise, this may as well be the case 
     38    logger = Log.getLogger ("mde.gui.WidgetManager"); 
     39 
     40    gui = new WidgetManager ("gui"); 
    4941} 
    5042 
    51 Gui gui;    // Currently just one instance; handle differently later. 
    52 // Handle externally or with a GUI Manager? 
     43WidgetManager gui; 
    5344 
    54 /** A GUI handles a bunch of windows, all to be drawn to the same device. */ 
    55 class Gui : IGui { 
    56     //BEGIN Methods for external use 
    57     //BEGIN Loading code 
    58     /** Load all windows from the file gui. */ 
    59     void load (char[] fileName) { 
    60         if (!confDir.exists (fileName)) { 
    61             logger.error ("Unable to load GUI: no config file!"); 
    62             return; // not a fatal error (so long as the game can run without a GUI!) 
    63         } 
     45 
     46/************************************************************************************************* 
     47 * The widget manager. 
     48 *  
     49 * This is responsible for loading and saving an entire gui (although more than one may exist), 
     50 * controlling the rendering device (e.g. the screen or a texture), and providing user input. 
     51 *  
     52 * Currently mouse coordinates are passed to widgets untranslated. It may make sense to translate 
     53 * them and possibly drop events for some uses, such as if the gui is drawn to a texture. 
     54 *  
     55 * Aside from the IWidgetManager methods, this class should be thread-safe. 
     56 *************************************************************************************************/ 
     57class WidgetManager : WidgetLoader { 
     58    /** Construct a new widget manager. 
     59     *  
     60     * params: 
     61     *  fileName = Name of file specifying the gui, excluding path and extension. 
     62     */ 
     63    this (char[] file) { 
     64        super(file); 
    6465         
    65         IReader reader; 
    66         try { 
    67             reader = confDir.makeMTReader (fileName, PRIORITY.HIGH_LOW, null, true); 
    68             reader.dataSecCreator = delegate mt.IDataSection(mt.ID id) { 
    69                 return new Window (id); 
    70             }; 
    71             reader.read; 
    72         } catch (Exception e) { 
    73             logger.error ("Unable to load GUI: errors parsing config file ("~confDir.getFileName(fileName,PRIORITY.HIGH_LOW)~"):"); 
    74             logger.error (e.msg); 
    75             throw new GuiException ("Failure parsing config file"); 
    76         } 
    77          
    78         // Get the renderer 
    79         char[]* p = "Renderer" in reader.dataset.header.Arg!(char[]); 
    80         if (p is null || *p is null) { 
    81             logger.warn ("no renderer specified: defaulting to Simple"); 
    82             rendName = "Simple"; 
    83         } 
    84         else 
    85             rendName = *p; 
    86          
    87         // get list 
    88         windows.length = reader.dataset.sec.length; // pre-allocate 
    89         windows.length = 0; 
    90         foreach (sec; reader.dataset.sec) { 
    91             Window w = cast(Window) sec; 
    92             debug if (w is null) { 
    93                 logger.error (__FILE__ ~ "(GUI.load): code error (w is null)"); 
    94                 continue; 
    95             } 
    96             try { 
    97                 w.finalise (this);  // if this fails, the window (but nothing else) won't work 
    98                 windows ~= w;       // only add if load successful 
    99             } catch (Exception e) { 
    100                 logger.error ("Window failed to load: " ~ e.msg); 
    101             } 
    102         } 
    103          
     66        // Events we want to know about: 
    10467        imde.input.addMouseClickCallback(&clickEvent); 
    10568        imde.input.addMouseMotionCallback(&motionEvent); 
    10669    } 
    10770     
    108     void save (char[] fileName) { 
    109         mt.DataSet ds = new mt.DataSet; 
    110          
    111         // Add header: 
    112         ds.header = new mt.DefaultData; 
    113         ds.header.Arg!(char[])["Renderer"] = rendName; 
    114          
    115         // Add windows to be saved: 
    116         foreach (window; windows) 
    117             ds.sec [window.name] = window; 
    118          
    119         try {   // Save 
    120             IWriter writer; 
    121             writer = confDir.makeMTWriter (fileName, ds); 
    122             writer.write; 
    123         } catch (mt.MTException e) { 
    124             logger.error ("Saving GUI failed:"); 
    125             logger.error (e.msg); 
    126              
    127             return; 
    128         } 
    129     } 
    130     //END Loading code 
    13171     
    132     /** Draw each window. 
    133     * 
    134     * Currently no concept of how to draw overlapping windows, or how to not bother drawing windows 
    135     * which don't need redrawing. */ 
     72    /** Draw the gui. */ 
    13673    void draw() { 
    137         foreach_reverse (w; windows)    // Draw, starting with back-most window. 
    138             w.draw; 
     74        synchronized(mutex) 
     75            child.draw; 
    13976    } 
    14077     
     78     
    14179    /** For mouse click events. 
    142    
    143     * Sends the event on to the relevant windows and all click callbacks. */ 
     80   
     81    * Sends the event on to the relevant windows and all click callbacks. */ 
    14482    void clickEvent (ushort cx, ushort cy, ubyte b, bool state) { 
    14583        debug scope (failure) 
    146                 logger.warn ("clickEvent: failed!"); 
     84            logger.warn ("clickEvent: failed!"); 
     85        mutex.lock; 
     86        scope(exit) mutex.unlock; 
    14787         
    14888        // NOTE: buttons receive the up-event even when drag-callbacks are in place. 
    14989        foreach (dg; clickCallbacks) 
    150             if (dg (cast(wdabs)cx, cast(wdabs)cy, b, state)) return;      // See IGui.addClickCallback's documentation 
     90            // See IWidgetManager.addClickCallback's documentation: 
     91            if (dg (cast(wdabs)cx, cast(wdabs)cy, b, state)) return; 
    15192         
     93        /+ 
    15294        foreach (i,w; windows) { 
    15395            IWidget widg = w.getWidget (cast(wdabs)cx,cast(wdabs)cy); 
     
    160102                return;     // only pass to first window 
    161103            } 
    162         } 
     104        }+/ 
    163105    } 
    164106     
    165107    /** For mouse motion events. 
    166    
    167     * Sends the event on to all motion callbacks. */ 
     108   
     109    * Sends the event on to all motion callbacks. */ 
    168110    void motionEvent (ushort cx, ushort cy) { 
    169111        debug scope (failure) 
    170112                logger.warn ("motionEvent: failed!"); 
     113        mutex.lock; 
     114        scope(exit) mutex.unlock; 
     115         
    171116        foreach (dg; motionCallbacks) 
    172117            dg (cast(wdabs)cx, cast(wdabs)cy); 
    173118    } 
    174119     
    175     //END Methods for external use 
    176120     
    177     //BEGIN IGui methods 
    178     char[] rendererName () { 
    179         return rendName; 
     121    void setSize (int x, int y) { 
     122        mutex.lock; 
     123        scope(exit) mutex.unlock; 
     124         
     125        w = cast(wdim) x; 
     126        h = cast(wdim) y; 
     127         
     128        if (child is null) 
     129            return;     // May not have been created before this is first run. 
     130        child.setWidth  (w, -1); 
     131        child.setHeight (h, -1); 
     132        child.setPosition (0,0); 
     133    } 
     134     
     135    //BEGIN IWidgetManager methods 
     136    // These methods are only intended for use within the gui package. They are not necessarily 
     137    // thread-safe. 
     138    IRenderer renderer () { 
     139        assert (rend !is null, "WidgetManager.renderer: rend is null"); 
     140        return rend; 
    180141    } 
    181142     
     
    190151        motionCallbacks[dg.ptr] = dg; 
    191152    } 
    192     void removeCallbacks (void* frame) { 
    193         clickCallbacks.remove(frame); 
    194         motionCallbacks.remove(frame); 
     153    void removeCallbacks (IChildWidget frame) { 
     154        clickCallbacks.remove(cast(void*) frame); 
     155        motionCallbacks.remove(cast(void*) frame); 
    195156    } 
    196     //END IGui methods 
     157    //END IWidgetManager methods 
     158     
     159protected: 
     160    /* Second stage of widget loading. */ 
     161    void createRootWidget () { 
     162        // The renderer needs to be created on the first load, but not after this. 
     163        if (rend is null) 
     164            rend = createRenderer (rendName); 
     165         
     166        child = makeWidget ("root"); 
     167         
     168        child.setWidth  (w, -1); 
     169        child.setHeight (h, -1); 
     170        child.setPosition (0,0); 
     171    } 
    197172     
    198173private: 
    199     Window[] windows;   // Windows. First window is "on top", others may be obscured. 
    200      
    201     char[] rendName;    // Name of renderer; for saving and creating renderers 
    202      
    203174    // callbacks indexed by their frame pointers: 
    204175    bool delegate(wdabs cx, wdabs cy, ubyte b, bool state) [void*] clickCallbacks; 
    205176    void delegate(wdabs cx, wdabs cy) [void*] motionCallbacks; 
     177    IRenderer rend; 
     178    wdim w,h;       // area available to the widgets 
    206179} 
  • mde/gui/exception.d

    r40 r75  
    3030} 
    3131 
    32 /// Thrown when something goes wrong while loading a window (usually a data error). 
    33 class WindowLoadException : GuiException 
    34 { 
    35     this (char[] msg) { 
    36         super(msg); 
    37     } 
    38 } 
    39  
    4032/// Thrown when createWidget or a Widget class's this() is called with invalid data. 
    41 class WidgetDataException : WindowLoadException 
     33class WidgetDataException : GuiException 
    4234{ 
    4335    this () {   // Default, by Widget class's this 
  • mde/gui/renderer/IRenderer.d

    r58 r75  
    1717module mde.gui.renderer.IRenderer; 
    1818 
    19 public import mde.gui.IGui; // wdim type is used by just about everything including this 
     19// Put here to avoid circular import. 
     20typedef int wdim; 
    2021 
    2122/** Interface for renderers. 
  • mde/gui/widget/Ifaces.d

    r72 r75  
    1414along with this program.  If not, see <http://www.gnu.org/licenses/>. */ 
    1515 
    16 /** Window and widget interfaces. */ 
     16/************************************************************************************************* 
     17 * Widget interfaces. 
     18 * 
     19 * Widgets are connected as the nodes of a tree. Widgets know their parent as a IParentWidget 
     20 * class and their children as IChildWidget classes. The gui manager is a special widget only 
     21 * implementing IParentWidget; all other widgets must implement IChildWidget and optionally 
     22 * IParentWidget. 
     23 *************************************************************************************************/ 
    1724module mde.gui.widget.Ifaces; 
    1825 
    1926public import mde.gui.renderer.IRenderer; 
    20 public import mde.gui.IGui; 
    21  
    22 /** Interface for Window, allowing widgets to call some of Window's methods. 
    23  * 
    24  * Contains the methods in Window available for widgets to call on their root. 
     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. */ 
     32alias 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. */ 
     48alias wdim  wdabs; 
     49alias wdim  wdrel;  /// ditto 
     50alias 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. */ 
     56struct wdimPair { 
     57    wdim x, y;  /// data 
     58
     59 
     60 
     61/************************************************************************************************* 
     62 * Common interface for all widgets. 
    2563 * 
    2664 * Notation: 
    2765 *  Positive/negative direction: along the x/y axis in this direction. 
    28  *  Layout widget: a widget containing multiple sub-widges (which hence controls how they are laid 
    29  *  out). */ 
    30 interface IWindow : IWidget 
    31 
    32     /** Widget ID type. Each ID is unique under this window. 
    33      * 
    34      * Type is int since this is the widget data type. */ 
    35     alias int widgetID; 
    36      
    37     /** Get a widget by ID. 
    38      * 
    39      * Returns the widget with the given ID from the Window's widget list. If the widget hasn't yet 
    40      * been created, creates it using the Window's widget creation data. */ 
    41     IWidget makeWidget (widgetID i); 
    42      
    43     /** Get a string from the widgetString associative array. */ 
    44     char[] getWidgetString (int i); 
    45      
    46     /** Add widget's saveData to the data to be saved, returning it's widgetID. */ 
    47     widgetID addCreationData (IWidget widget); 
    48      
    49     /** Add a string to the widgetString associative array, returning it's index. */ 
    50     int addWidgetString (char[] str); 
    51      
    52     /** Returns the window's gui. */ 
    53     IGui gui (); 
    54      
    55     /** The widget/window needs redrawing. */ 
     66 *  Layout widget: a widget containing multiple sub-widges (which hence controls how they are 
     67 *  laid out). 
     68 *************************************************************************************************/ 
     69//NOTE: keep this? 
     70interface IWidget 
     71
     72
     73 
     74 
     75/************************************************************************************************* 
     76 * Interface for the widget manager. 
     77 *  
     78 * This class handles widget rendering, input, loading and saving. 
     79 *************************************************************************************************/ 
     80interface IWidgetManager : IParentWidget 
     81
     82    // Loading/saving: 
     83    /** Create a widget by ID. 
     84     * 
     85     * Creates a widget, using the widget data with index id. Widget data is loaded from files, 
     86     * and per design (multiple gui layouts, called designs, may exist; data is per design). 
     87     *  
     88     * Note: this method is only for "named" widgets; generic widgets instanciated in lists are 
     89     * created differently. */ 
     90    IChildWidget makeWidget (widgetID id); 
     91     
     92    /** Record some changes, for saving. */ 
     93    void setData (widgetID id, WidgetData); 
     94     
     95    // Rendering: 
     96    /** For when a widget needs redrawing. 
     97     *  
     98     * Must be called because rendering may only be done on events. 
     99     *  
     100     * Currently it just causes everything to be redrawn next frame. */ 
    56101    void requestRedraw (); 
    57102     
     
    61106    * provides the possibility of per-window renderers (if desired). */ 
    62107    IRenderer renderer (); 
    63 
    64  
    65 /** Interface for widgets. 
    66  * 
    67  * Note that Window also implements this interface so that widgets can interact with their parent 
    68  * in a uniform way. 
     108     
     109     
     110    // User input: 
     111    /** Add a mouse click callback: delegate will be called for all mouse click events recieved. 
     112     * 
     113     * The delegate should return true if it accepts the event and no further processing is 
     114     * required (i.e. the event should not be handled by anything else), false otherwise. 
     115     *  
     116     * Note that this is not a mechanism to prevent unwanted event handling, and in the future 
     117     * may be removed (so event handling cannot be cut short). */ 
     118    void addClickCallback (bool delegate (wdabs cx, wdabs cy, ubyte b, bool state) dg); 
     119     
     120    /** Add a mouse motion callback: delegate will be called for all motion events recieved. */ 
     121    void addMotionCallback (void delegate (wdabs cx, wdabs cy) dg); 
     122     
     123    // FIXME: keyboard callback (letter only, for text input? Also used for setting keybindings though...) 
     124     
     125    /** Remove all event callbacks on this widget (according to the delegate's .ptr). */ 
     126    void removeCallbacks (IChildWidget frame); 
     127
     128 
     129 
     130/************************************************************************************************* 
     131 * Interface for parent widgets, including the gui manager. 
     132 * 
     133 * A widget may call these methods on its parent, and on the gui manager. 
     134 *************************************************************************************************/ 
     135interface IParentWidget : IWidget 
     136
     137    // NOTE: Likely some day this interface will really be used. 
     138    // NOTE: What widget is NOT going to implement this? It will probably be inherited. 
     139
     140 
     141 
     142/************************************************************************************************* 
     143 * Interface for (child) widgets, i.e. all widgets other than the manager. 
    69144 * 
    70145 * A widget is a region of a GUI window which handles rendering and user-interaction for itself 
    71  * and is able to communicate with it's window and parent/child widgets as necessary. 
    72  * 
    73  * If a widget is to be creatable by Window.makeWidget, it must be listed in the createWidget 
    74  * module, have a constructor of the following form, and should implement getCreationData(). 
    75  * Use Ddoc to explain what initialization data is used. 
     146 * and is able to communicate with its manager and parent/child widgets as necessary. 
     147 * 
     148 * If a widget is to be creatable by IWidgetManager.makeWidget, it must be listed in the 
     149 * createWidget module, have a constructor of the following form, and should update it's 
     150 * creation data as necessary via IWidgetManager.setData(). 
     151 * It should use Ddoc to explain what initialization data is used. 
    76152 * ---------------------------------- 
    77153 * /++ Constructor for a ... widget. 
     
    80156 *  + [widgetID, x, y] 
    81157 *  + where x is ... and y is ... +/ 
    82  * this (IWindow window, int[] data); 
     158 * this (IWidgetManager mgr, WidgetData data); 
    83159 * ---------------------------------- 
    84  * Where window is the root window (the window to which the widget belongs) and data is an array of 
    85  * initialisation data. The method should throw a WidgetDataException (created without parameters) 
    86  * if the data has wrong length or is otherwise invalid. 
    87  * 
    88  * The widget's size should be set either by it's this() method or by the first call to 
    89  * setSize(). setSize() is called on all widgets immediately after their creation, and throwing an 
    90  * exception at this point (but not on later calls to setSize) is an acceptible method of failure
    91  *
     160 * Where mgr is the widget manager and data is 
     161 * initialisation data. The method should throw a WidgetDataException (created without 
     162 * parameters) if the data has wrong length or is otherwise invalid. 
     163 * 
     164 * A parent widget is responsible for setting the size of its children widgets, however it must 
     165 * satisfy their minimal sizes as available from minWidth() and minHeight(). setWidth() and 
     166 * setHeight() are called on all widgets after creation
     167 *************************************************************************************************
    92168//NOTE: add another this() without the data for default initialization, for the GUI editor? 
    93 interface IWidget 
     169interface IChildWidget : IWidget 
    94170{ 
    95171//BEGIN Load and save 
    96     /** Called after creating widgets to adjust size & other mutable attributes from saved data. 
    97      * 
    98      * As for setSize, setPosition should be called afterwards. 
    99      * 
    100      * Each widget should call adjust on each of its sub-widgets in turn with data, each time 
    101      * replacing data by the return value of the call. It should then take its own mutable data 
    102      * from the beginning of the array and return the remainder of the array. 
    103      * 
    104      * Adjust should handle errors gracefully by reverting to default values and not throwing. 
    105      * This is because the creation data and the user's mutable data may be stored separately and 
    106      * become out-of-sync during an update. */ 
    107     int[] adjust (int[] data); 
    108      
    109     /** Output data suitible for recreating the widget (data to be passed to this()). 
    110      * 
    111      * Function may need to call Window's addCreationData() and addWidgetString() methods to save 
    112      * other data. 
    113      * 
    114      * Creation data is data only changed when the gui is edited. */ 
    115     int[] getCreationData (); 
    116      
    117     /** Output data containing the widget's current adjustments (data to be passed to adjust()). 
    118      * 
    119      * Mutable data is data which can be changed during normal gui use, such as the size of 
    120      * resizible widgets or current tab of a tab widget. 
    121      * 
    122      * Should be a concatenation of each sub-widget's mutable data and the widget's own. */ 
    123     int[] getMutableData (); 
     172    /** Called when the renderer is changed (at least when the changes affect dimensions). 
     173     * Also called after widget creation, before any other methods are called. 
     174     *  
     175     * Returns: true when widget's dimensions (may) have changed. 
     176     *  
     177     * Should be propegated down to all child widgets. */ 
     178    bool rendererChanged (); 
     179     
     180    /+ Use when widget editing is available? Requires widgets to know their parents. 
     181    /** Called when a child widget's size has changed. 
     182     *  
     183     * Should be propegated up to parents. */ 
     184    void childChanged (); 
     185    +/ 
    124186//END Load and save 
    125187     
     
    137199    wdim minHeight ();  /// ditto 
    138200     
    139     /** Get the current size of the widget. */ 
    140     void getCurrentSize (out wdim w, out wdim h); 
     201    /** Get the current size of the widget. 
     202     *  
     203     * Deprecated: is it needed now? 
     204     */ 
     205    deprecated void getCurrentSize (out wdim w, out wdim h); 
    141206     
    142207    /** Used to adjust the size. 
     
    149214     *        Most widgets can simply ignore it. 
    150215     * 
    151      * Implementation: 
    152      * The size should be clamped to the widget's minimal size, i.e. the size set may be larger 
    153      * than that given by the parameters. Conversely, the size should not be reduced to the 
    154      * widget's maximal size (if any) but expanded as necessary (alignment to be implemented). 
    155      * This should be true for both resizable and fixed widgets; fixed widgets may still be scaled 
    156      * to fill a whole row/column in a layout widget. 
    157      * 
    158      * If the actual size is needed, call getCurrentSize afterwards. setPosition must be called 
    159      * afterwards if the widget might be a layout widget. */ 
     216     * If called with dimensions less than minWidth/minHeight return: the widget may set its size 
     217     * to either the dimension given or its minimal dimension (even though this is larger). If the 
     218     * larger size is set, events won't be received in the extra area. FIXME: sort out rendering. 
     219     * Otherwise, the dimensions should always be set exactly. 
     220     * 
     221     * setPosition must be called after calling either setWidth or setHeight. */ 
    160222    void setWidth (wdim nw, int dir); 
    161223    void setHeight (wdim nh, int dir);  /// ditto 
     
    192254    void draw (); 
    193255} 
     256 
     257 
     258/************************************************************************************************* 
     259 * The data type all widgets creatable by the widget manager receive on creation. 
     260 *  
     261 * Conversion code to/from MT tags is contained in the addTag and writeAll methods of 
     262 * WidgetDataSet and WidgetDataChanges. 
     263 *************************************************************************************************/ 
     264struct WidgetData 
     265{ 
     266    int[]   ints;   // An array of integer data 
     267    char[]  str;    // One string 
     268} 
  • mde/gui/widget/TextWidget.d

    r72 r75  
    6969     * where contentID is an ID for the string ID of the contained content 
    7070     * and colour is an 8-bit-per-channel RGB colour of the form 0xRRGGBB. */ 
    71     this (IWindow wind, int[] data) { 
    72         if (data.length != 3) throw new WidgetDataException; 
    73         text.set (wind.getWidgetString(data[1]), data[2]); 
     71    this (IWidgetManager mgr, WidgetData data) { 
     72        if (data.length != 2) throw new WidgetDataException; 
     73        text.set (data.str, data[1]); 
    7474        text.getDimensions (mw, mh); 
    75         super (wind,data); 
     75        super (mgr,data); 
    7676    } 
    7777     
  • mde/gui/widget/Widget.d

    r66 r75  
    2929* useful basic implementation for widgets. Widgets need not inherit these (they only need implement 
    3030* IWidget); they are simply provided for convenience and to promote code reuse. */ 
    31 abstract class Widget : IWidget 
     31abstract class Widget : IChildWidget 
    3232{ 
    3333//BEGIN Load and save 
    34     // Base this(). All widgets must check data.length is correct before calling this method. 
    35     // The widget ID is saved to widgetType, for correct saving. 
    36     this (IWindow wind, int[] data) { 
    37         window = wind; 
    38         widgetType = data[0]; 
     34    // Base this() for child Widgets. 
     35    this (IWidgetManager mgr, WidgetData data) { 
     36        this.mgr = mgr; 
    3937    } 
    4038     
    41     // Most widgets don't need to do adjustments based on mutable data, however they usually do 
    42     // still need to set their size. 
    43     int[] adjust (int[] data) { 
    44         setWidth (0,-1); 
    45         setHeight (0,-1); 
    46         return data; 
    47     } 
    48      
    49     // Widget type should always be the first value. Any widget using extra creation data will need 
    50     // to reimplemnt this method. 
    51     int[] getCreationData () { 
    52         return [widgetType]; 
    53     } 
    54     // Most widgets don't use mutable data. 
    55     int[] getMutableData () { 
    56         return []; 
     39    // Very basic implementation which assumes the renderer cannot affect the widget's size. 
     40    bool rendererChanged () { 
     41        return false; 
    5742    } 
    5843//END Load and save 
     
    7055    } 
    7156     
    72     void getCurrentSize (out wdim cw, out wdim ch) { 
     57    deprecated void getCurrentSize (out wdim cw, out wdim ch) { 
    7358        cw = w; 
    7459        ch = h; 
     
    10388    /* Basic draw method: draw the background (all widgets should do this). */ 
    10489    void draw () { 
    105         window.renderer.drawWidgetBack (x,y, w,h); 
     90        mgr.renderer.drawWidgetBack (x,y, w,h); 
    10691    } 
    10792     
    10893protected: 
    109     final int widgetType;   // the type (stored for saving) 
    110     IWindow window;     // the enclosing window 
     94    IWidgetManager mgr;     // the enclosing window 
    11195    wdim x, y;          // position 
    11296    wdim w, h;          // size 
     
    123107     * [widgetID, w, h] 
    124108     * where w, h is the fixed size. */ 
    125     this (IWindow wind, int[] data) { 
    126         mw = cast(wdim) data[1]
    127         mh = cast(wdim) data[2]; 
    128         super (wind, data)
     109    this (IWidgetManager mgr, WidgetData data) { 
     110        super (mgr, data)
     111        mw = cast(wdim) data.ints[1]; 
     112        mh = cast(wdim) data.ints[2]
    129113        w = mw; 
    130114        h = mh; 
    131115    } 
    132      
    133     int[] getCreationData () { 
    134         return [widgetType, mw, mh]; 
    135     } 
    136116} 
     117 
    137118/** A base for resizable widgets. */ 
    138119class SizableWidget : Widget { 
    139120    // Check data.length is at least 1 before calling! 
    140121    /// Constructor for a completely resizable [blank] widget. 
    141     this (IWindow wind, int[] data) { 
    142         super (wind, data); 
     122    this (IWidgetManager mgr, WidgetData data) { 
     123        super (mgr, data); 
    143124    } 
    144125     
  • mde/gui/widget/Window.d

    r74 r75  
    9898        yh = y+h; 
    9999    } 
    100     //BEGIN Mergetag code 
    101     void addTag (char[] tp, mt.ID id, char[] dt) { 
    102         // Priority is HIGH_LOW, so don't overwrite data which has already been loaded. 
    103         if (tp == "int[][int]") { 
    104             if (id == "widgetData" && widgetData == null) { 
    105                 widgetData = cast(int[][widgetID]) parseTo!(int[][int]) (dt); 
    106             } 
    107         } else if (tp == "char[][int]") { 
    108             if (id == "widgetStrings" && widgetStrings == null) { 
    109                 widgetStrings = parseTo!(char[][int]) (dt); 
    110             } 
    111         } else if (tp == "int[]") { 
    112             if (id == "mutableData" && mutableData == null) { 
    113                 mutableData = parseTo!(int[]) (dt); 
    114             } 
    115         } else if (tp == "int") { 
    116             if (id == "x" && x == -1) { 
    117                 x = cast(wdim) parseTo!(int) (dt); 
    118             } else if (id == "y" && y == -1) { 
    119                 y = cast(wdim) parseTo!(int) (dt); 
    120             } 
    121         } 
    122     } 
    123     void writeAll (ItemDelg dlg) 
    124     in { 
    125         assert (widgetData is null, "Window.writeAll: widgetData !is null"); 
    126     } body { 
    127         /+ NOTE: currently editing is impossible... 
    128         if (edited) {   // only save the widget creation data if it's been adjusted: 
    129             addCreationData (widget);   // generate widget save data