egra: cosmetix, added brief comments for most interesting widget methods
[iv.d.git] / egra / gui / widgets / root.d
blobb74604a55839ab0009c1c6f8374db140124d0985
1 /*
2 * Simple Framebuffer Gfx/GUI lib
4 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
5 * Understanding is not required. Only obedience.
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, version 3 of the License ONLY.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 module iv.egra.gui.widgets.root /*is aliced*/;
20 private:
22 import arsd.simpledisplay;
24 import iv.egra.gfx;
26 import iv.alice;
27 import iv.cmdcon;
28 import iv.dynstring;
29 import iv.strex;
30 import iv.utfutil;
32 import iv.egra.gui.subwindows;
33 import iv.egra.gui.widgets.base;
34 import iv.egra.gui.widgets.buttons : BaseButtonWidget;
37 // ////////////////////////////////////////////////////////////////////////// //
38 // this is root widget that is used for subwindow client area
39 public class RootWidget : Widget {
40 SubWindow owner;
41 uint mButtons; // used for grab
43 override EgraStyledClass getParent () nothrow @trusted @nogc { return owner; }
45 @disable this ();
47 this (SubWindow aowner) {
48 childDir = GxDir.Vert;
49 super(null);
50 owner = aowner;
51 if (aowner !is null) aowner.setRoot(this);
54 override bool isOwnerFocused () nothrow @safe @nogc {
55 if (owner is null) return false;
56 return owner.active;
59 override bool isOwnerInWindowList () nothrow @safe @nogc {
60 if (owner is null) return false;
61 return owner.inWindowList;
64 // can be called by the owner
65 // this also resets pressed mouse buttons
66 override void releaseGrab () {
67 mButtons = 0;
70 override GxPoint getGlobalOffset () nothrow @safe {
71 if (owner is null) return super.getGlobalOffset();
72 return GxPoint(owner.x0+owner.clientOffsetX, owner.y0+owner.clientOffsetY)+rect.pos;
75 void checkGrab () {
76 if (mButtons && !isOwnerFocused) releaseGrab();
79 bool hasGrab () {
80 checkGrab();
81 return (mButtons != 0);
84 // this is called by the owning window when the window is added to the window list
85 // it is used to set focus to the first properly focusable child if nothing was focused yet
86 void fixFocus () {
87 Widget fc = focusedWidget;
88 if (fc is null || fc is this || !fc.canAcceptFocus()) moveFocusForward();
89 fc = focusedWidget;
90 //if (fc is null) conwriteln("FUCK!"); else conwriteln("FOCUSED: ", typeid(fc).name);
93 // move focus backward (if it is not obvious yet)
94 void moveFocusBackward () {
95 Widget pf = null;
96 Widget fc = focusedWidget;
97 foreach (Widget w; allVisualDepth) {
98 if (w is fc) break;
99 if (w.canAcceptFocus()) pf = w;
102 if (pf is null) {
103 // find last
104 foreach (Widget w; allVisualDepth) {
105 if (w.canAcceptFocus()) pf = w;
109 if (pf !is null) pf.focus();
112 // move focus forward (if it is not obvious yet)
113 void moveFocusForward () {
114 Widget fc = focusedWidget;
115 bool seenFC = (fc is null);
116 Widget nf = null;
117 foreach (Widget w; allVisualDepth) {
118 if (!seenFC) {
119 seenFC = (w is fc);
120 } else {
121 if (w.canAcceptFocus()) { nf = w; break; }
125 if (nf is null) {
126 // find first
127 foreach (Widget w; allVisualDepth) {
128 if (w.canAcceptFocus()) { nf = w; break; }
132 if (nf !is null) nf.focus();
136 override bool onKeyBubble (Widget dest, KeyEvent event) {
137 if (event.pressed) {
138 if (event == "Tab" || event == "C-Tab") { moveFocusForward(); return true; }
139 if (event == "S-Tab" || event == "C-S-Tab") { moveFocusBackward(); return true; }
141 Widget def;
142 immutable bool isDefAccept = (event == "Enter");
143 immutable bool isDefCancel = (event == "Escape");
145 // check hotkeys
146 Widget hk = null;
147 foreach (Widget w; allVisualDepth) {
148 if (w.isMyHotkey(event) && w.hotkeyActivated()) {
149 def = null;
150 hk = w;
151 break;
153 if (def is null) {
154 if (isDefAccept || isDefCancel) {
155 if (auto btn = cast(BaseButtonWidget)w) {
156 if ((isDefAccept && btn.deftype == BaseButtonWidget.Default.Accept) ||
157 (isDefCancel && btn.deftype == BaseButtonWidget.Default.Cancel))
159 if (btn.canAcceptFocus()) def = w;
166 if (hk !is null) return true;
167 if (def !is null && def.hotkeyActivated()) return true;
170 return super.onKeyBubble(dest, event);
173 // dispatch keyboard event
174 bool dispatchKey (KeyEvent event) {
175 checkGrab();
176 return dispatchTo(focusedWidget, delegate bool (Widget curr, Widget dest, EventPhase phase) {
177 if (curr.nonVisual) return false;
178 final switch (phase) {
179 case EventPhase.Sink: return curr.onKeySink(dest, event);
180 case EventPhase.Mine: return curr.onKey(event);
181 case EventPhase.Bubble: return curr.onKeyBubble(dest, event);
183 assert(0); // just in case
187 // this is quite complicated...
188 // find proper destination for mouse event according to current grab
189 // also, updates the current grab
190 protected Widget getMouseDestination (in MouseEvent event) {
191 checkGrab();
193 // still has a grab?
194 if (mButtons) {
195 // if some mouse buttons are still down, route everything to the focused widget
196 // also, release a grab here if we can (grab flag is not used anywhere else)
197 if (event.type == MouseEventType.buttonReleased) mButtons &= ~cast(uint)event.button;
198 return focusedWidget;
201 Widget dest = childAt(event.x, event.y);
202 assert(dest !is null);
204 // if mouse button is pressed, and there were no pressed buttons before,
205 // find the child, and check if it can grab events
206 if (event.type == MouseEventType.buttonPressed) {
207 if (dest !is focusedWidget) dest.focus();
208 if (dest is focusedWidget) {
209 // this activates the grab
210 mButtons = cast(uint)event.button;
211 } else {
212 releaseGrab();
214 } else {
215 // release grab, if necessary (it shouldn't be necessary here, but just in case...)
216 if (mButtons && event.type == MouseEventType.buttonReleased) {
217 mButtons &= ~cast(uint)event.button;
221 // route to the proper child
222 return dest;
225 // dispatch mouse event
226 // mouse event coords should be relative to our rect
227 bool dispatchMouse (MouseEvent event) {
228 Widget dest = getMouseDestination(event);
229 assert(dest !is null);
230 // convert event to global
231 immutable GxRect grect = globalRect;
232 event.x += grect.pos.x;
233 event.y += grect.pos.y;
234 return dispatchTo(dest, delegate bool (Widget curr, Widget dest, EventPhase phase) {
235 if (curr.nonVisual) return false;
236 // fix coordinates
237 immutable GxRect wrect = curr.globalRect;
238 MouseEvent ev = event;
239 ev.x -= wrect.pos.x;
240 ev.y -= wrect.pos.y;
241 final switch (phase) {
242 case EventPhase.Sink: return curr.onMouseSink(dest, ev);
243 case EventPhase.Mine: return curr.onMouse(ev);
244 case EventPhase.Bubble: return curr.onMouseBubble(dest, ev);
246 assert(0); // just in case
250 // dispatch char event
251 bool dispatchChar (dchar ch) {
252 checkGrab();
253 return dispatchTo(focusedWidget, delegate bool (Widget curr, Widget dest, EventPhase phase) {
254 if (curr.nonVisual) return false;
255 final switch (phase) {
256 case EventPhase.Sink: return curr.onCharSink(dest, ch);
257 case EventPhase.Mine: return curr.onChar(ch);
258 case EventPhase.Bubble: return curr.onCharBubble(dest, ch);
260 assert(0); // just in case