Mercurial > projects > mde
view mde/gui/WMScreen.d @ 128:41582439a42b
Added support for dynamic EnumContent loading and saving, with translation loading.
WMScreen.init removed; code moved to this() since class is now created by main() instead of a static this().
Fix for SwitchWidget not passing events. Still some resizing bugs evident in SwitchWidget :-(
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Wed, 14 Jan 2009 20:24:14 +0000 |
parents | c9843fbaac88 |
children | 9cff74f68b84 |
line wrap: on
line source
/* LICENSE BLOCK Part of mde: a Modular D game-oriented Engine Copyright © 2007-2008 Diggory Hardy This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /************************************************************************************************* * A gui manager class using mde.setup.Screen and mde.input.Input. * * This is the module to use externally to create a graphical user interface (likely also with * content modules). *************************************************************************************************/ module mde.gui.WMScreen; import mde.gui.WidgetManager; import mde.gui.widget.Ifaces; import mde.gui.renderer.createRenderer; import mde.setup.Screen; import tango.util.log.Log : Log, Logger; private Logger logger; static this () { logger = Log.getLogger ("mde.gui.WMScreen"); } /************************************************************************************************* * The widget manager. * * This provides a layer on top of WidgetLoader, handling input and rendering. Other functionality * is contained in the super class, to simplify supporting new input/graphics libraries. * * Currently mouse coordinates are passed to widgets untranslated. It may make sense to translate * them and possibly drop events for some uses, such as if the gui is drawn to a texture. * * Aside from the IWidgetManager methods, this class should be thread-safe. *************************************************************************************************/ scope class WMScreen : AWidgetManager, Screen.IDrawable { /** Construct a new widget manager. * * Must be run after static this. * * params: * fileName = Name of file specifying the gui, excluding path and extension. */ this (char[] file) { // Doesn't need a lock - cannot conflict with other class functions. super(file); Screen.addDrawable (this); // Events we want to know about: imde.input.addMouseClickCallback(&clickEvent); imde.input.addMouseMotionCallback(&motionEvent); } /** Draw the gui. */ void draw() { synchronized(mutex) { if (child) child.draw; foreach (popup; popups) popup.widget.draw(); } } /** For mouse click events. * * Sends the event on to the relevant windows and all click callbacks. */ void clickEvent (ushort usx, ushort usy, ubyte b, bool state) { debug scope (failure) logger.warn ("clickEvent: failed!"); mutex.lock; scope(exit) mutex.unlock; if (child is null) return; wdabs cx = cast(wdabs) usx, cy = cast(wdabs) usy; // 1. Callbacks have the highest priority recieving events (e.g. a button release) foreach (dg; clickCallbacks) if (dg (cx, cy, b, state)) return; // 2. Then pop-ups: close from top, depending on click pos // Note: assumes each evaluated popup's parent is not under another still open popup. // Also assumes popup's parent doesn't have other children in its box. size_t removeTo = popups.length; bool eventDone; // don't pass clickEvent IChildWidget widg; // widget clicked on foreach_reverse (i,popup; popups) with (popup) { if (cx < x || cx >= x + w || cy < y || cy >= y + h) { // on popup if (parent.onSelf (cx, cy)) { if (parent.popupParentClick()) removeTo = i; eventDone = true; break; } else { removeTo = i; parent.popupClose; } } else { widg = widget.getWidget (cast(wdabs)cx,cast(wdabs)cy); break; } } if (removeTo < popups.length) { requestRedraw; popups = popups[0..removeTo]; } if (eventDone) return; // 3. Then the main widget tree debug assert (cx < child.width && cy < child.height, "WidgetManager: child doesn't cover whole area (code error)"); if (widg is null) widg = child.getWidget (cast(wdabs)cx,cast(wdabs)cy); if (keyFocus && keyFocus !is widg) { keyFocus.keyFocusLost; keyFocus = null; imde.input.setLetterCallback (null); } if (widg !is null) { if (widg.clickEvent (cast(wdabs)cx,cast(wdabs)cy,b,state) & 1) { keyFocus = widg; imde.input.setLetterCallback (&widg.keyEvent); } } } /** For mouse motion events. * * Sends the event on to all motion callbacks. */ void motionEvent (ushort scx, ushort scy) { debug scope (failure) logger.warn ("motionEvent: failed!"); mutex.lock; scope(exit) mutex.unlock; wdabs cx = cast(wdabs) scx, cy = cast(wdabs) scy; foreach (dg; motionCallbacks) dg (cx, cy); IChildWidget ohighlighted = highlighted; foreach_reverse (popup; popups) with (popup) { if (cx >= x && cx < x+w && cy >= y && cy < y+h) { highlighted = widget.getWidget (cx,cy); goto foundPopup; } } highlighted = null; // not over a popup foundPopup: if (ohighlighted != highlighted) { if (ohighlighted) ohighlighted.highlight (false); if (highlighted) highlighted.highlight (true); requestRedraw; } } void sizeEvent (int nw, int nh) { // IDrawable function mutex.lock; scope(exit) mutex.unlock; w = cast(wdim) nw; h = cast(wdim) nh; if (w < mw) { logger.warn ("Min width for gui, {}, not met: {}", mw, w); w = mw; } if (h < mh) { logger.warn ("Min height for gui, {}, not met: {}", mh, h); h = mh; } if (!child) return; // if not created yet. child.setWidth (w, -1); child.setHeight (h, -1); child.setPosition (0,0); } protected: /* Second stage of widget loading. * Note: sizeEvent should be called with window size before this. */ final override void createRootWidget () { // The renderer needs to be created on the first load, but not after this. if (rend is null) rend = createRenderer (rendName); popups = null; debug (mdeWidgets) logger.trace ("Creating root widget..."); child = makeWidget (this, "root"); debug (mdeWidgets) logger.trace ("Setting up root widget..."); child.setup (0, 3); mw = child.minWidth; mh = child.minHeight; if (w < mw) { logger.warn ("Min width for gui, {}, not met: {}", mw, w); w = mw; } if (h < mh) { logger.warn ("Min height for gui, {}, not met: {}", mh, h); h = mh; } debug (mdeWidgets) logger.trace ("Setting size and position of root widget..."); child.setWidth (w, -1); child.setHeight (h, -1); child.setPosition (0,0); debug (mdeWidgets) logger.trace ("Done creating root widget."); } final override void preSave () { if (keyFocus) { keyFocus.keyFocusLost; keyFocus = null; imde.input.setLetterCallback (null); } } }