Mercurial > projects > mde
view mde/gui/WMScreen.d @ 122:f96e8d18c00a
Missed file from last commit.
author | Diggory Hardy <diggory.hardy@gmail.com> |
---|---|
date | Fri, 02 Jan 2009 18:10:14 +0000 |
parents | |
children | c9843fbaac88 |
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 Items = mde.content.Items; // loadTranslation import mde.lookup.Options; // miscOpts.L10n callback 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. * * params: * fileName = Name of file specifying the gui, excluding path and extension. */ this (char[] file) { super(file); Screen.addDrawable (this); clickCallbacks = new typeof(clickCallbacks); motionCallbacks = new typeof(motionCallbacks); } // this() runs during static this(), when imde.input doesn't exist. init() runs later. void init () { // Doesn't need a lock - cannot conflict with other class functions. // Events we want to know about: imde.input.addMouseClickCallback(&clickEvent); imde.input.addMouseMotionCallback(&motionEvent); Items.loadTranslation (); miscOpts.L10n.addCallback (&reloadStrings); } /** 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 || h < mh) logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h); 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 || h < mh) logger.warn ("Minimal dimensions ({},{}) not met: ({},{}), but I cannot resize myself!",mw,mh,w,h); 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); } } }