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*/;
22 import arsd
.simpledisplay
;
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
{
41 uint mButtons
; // used for grab
43 override EgraStyledClass
getParent () nothrow @trusted @nogc { return owner
; }
47 this (SubWindow aowner
) {
48 childDir
= GxDir
.Vert
;
51 if (aowner
!is null) aowner
.setRoot(this);
54 override bool isOwnerFocused () nothrow @safe @nogc {
55 if (owner
is null) return false;
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 () {
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
;
76 if (mButtons
&& !isOwnerFocused
) releaseGrab();
81 return (mButtons
!= 0);
90 // dispatch event to this widget
91 // it implements sink/bubble model
93 // bool delegate (Widget curr, Widget dest, EventPhase phase);
94 // returns "event eaten" flag (which stops propagation)
95 // `dest` must be a good child, and cannot be `null`
96 final bool dispatchTo(DG
) (Widget dest
, scope DG dg
)
97 if (is(typeof((inout int=0) { DG dg
= void; Widget w
; EventPhase ph
; immutable bool res
= dg(w
, w
, ph
); })))
99 // as we're walking from the bottom, first call it recursively, and then call delegate
100 bool sinkPhase() (Widget curr
) {
101 if (curr
is null) return false;
102 if (sinkPhase(curr
.parent
)) return true;
103 return dg(curr
, dest
, EventPhase
.Sink
);
106 if (dest
is null ||
!isMyChild(dest
)) return false;
108 if (sinkPhase(dest
.parent
)) return true;
109 if (dg(dest
, dest
, EventPhase
.Mine
)) return true;
110 for (Widget w
= dest
.parent
; w
!is null; w
= w
.parent
) {
111 if (dg(w
, dest
, EventPhase
.Bubble
)) return true;
119 Widget fc
= focusedWidget
;
120 foreach (Widget w
; allVisualDepth
) {
122 if (w
.canAcceptFocus()) pf
= w
;
127 foreach (Widget w
; allVisualDepth
) {
128 if (w
.canAcceptFocus()) pf
= w
;
132 if (pf
!is null) pf
.focus();
136 Widget fc
= focusedWidget
;
137 bool seenFC
= (fc
is null);
139 foreach (Widget w
; allVisualDepth
) {
143 if (w
.canAcceptFocus()) { nf
= w
; break; }
149 foreach (Widget w
; allVisualDepth
) {
150 if (w
.canAcceptFocus()) { nf
= w
; break; }
154 if (nf
!is null) nf
.focus();
157 override bool onKeyBubble (Widget dest
, KeyEvent event
) {
159 if (event
== "Tab" || event
== "C-Tab") { doTab(); return true; }
160 if (event
== "S-Tab" || event
== "C-S-Tab") { doShiftTab(); return true; }
163 immutable bool isDefAccept
= (event
== "Enter");
164 immutable bool isDefCancel
= (event
== "Escape");
168 foreach (Widget w
; allVisualDepth
) {
169 if (w
.isMyHotkey(event
) && w
.hotkeyActivated()) {
175 if (isDefAccept || isDefCancel
) {
176 if (auto btn
= cast(BaseButtonWidget
)w
) {
177 if ((isDefAccept
&& btn
.deftype
== BaseButtonWidget
.Default
.Accept
) ||
178 (isDefCancel
&& btn
.deftype
== BaseButtonWidget
.Default
.Cancel
))
180 if (btn
.canAcceptFocus()) def
= w
;
187 if (hk
!is null) return true;
188 if (def
!is null && def
.hotkeyActivated()) return true;
191 return super.onKeyBubble(dest
, event
);
194 bool dispatchKey (KeyEvent event
) {
196 return dispatchTo(focusedWidget
, delegate bool (Widget curr
, Widget dest
, EventPhase phase
) {
197 if (curr
.nonVisual
) return false;
198 final switch (phase
) {
199 case EventPhase
.Sink
: return curr
.onKeySink(dest
, event
);
200 case EventPhase
.Mine
: return curr
.onKey(event
);
201 case EventPhase
.Bubble
: return curr
.onKeyBubble(dest
, event
);
203 assert(0); // just in case
207 // this is quite complicated...
208 protected Widget
getMouseDestination (in MouseEvent event
) {
213 // if some mouse buttons are still down, route everything to the focused widget
214 // also, release a grab here if we can (grab flag is not used anywhere else)
215 if (event
.type
== MouseEventType
.buttonReleased
) mButtons
&= ~cast(uint)event
.button
;
216 return focusedWidget
;
219 Widget dest
= childAt(event
.x
, event
.y
);
220 assert(dest
!is null);
222 // if mouse button is pressed, and there were no pressed buttons before,
223 // find the child, and check if it can grab events
224 if (event
.type
== MouseEventType
.buttonPressed
) {
225 if (dest
!is focusedWidget
) dest
.focus();
226 if (dest
is focusedWidget
) {
227 // this activates the grab
228 mButtons
= cast(uint)event
.button
;
233 // release grab, if necessary (it shouldn't be necessary here, but just in case...)
234 if (mButtons
&& event
.type
== MouseEventType
.buttonReleased
) {
235 mButtons
&= ~cast(uint)event
.button
;
239 // route to the proper child
243 // mouse event coords should be relative to our rect
244 bool dispatchMouse (MouseEvent event
) {
245 Widget dest
= getMouseDestination(event
);
246 assert(dest
!is null);
247 // convert event to global
248 immutable GxRect grect
= globalRect
;
249 event
.x
+= grect
.pos
.x
;
250 event
.y
+= grect
.pos
.y
;
251 return dispatchTo(dest
, delegate bool (Widget curr
, Widget dest
, EventPhase phase
) {
252 if (curr
.nonVisual
) return false;
254 immutable GxRect wrect
= curr
.globalRect
;
255 MouseEvent ev
= event
;
258 final switch (phase
) {
259 case EventPhase
.Sink
: return curr
.onMouseSink(dest
, ev
);
260 case EventPhase
.Mine
: return curr
.onMouse(ev
);
261 case EventPhase
.Bubble
: return curr
.onMouseBubble(dest
, ev
);
263 assert(0); // just in case
267 bool dispatchChar (dchar ch
) {
269 return dispatchTo(focusedWidget
, delegate bool (Widget curr
, Widget dest
, EventPhase phase
) {
270 if (curr
.nonVisual
) return false;
271 final switch (phase
) {
272 case EventPhase
.Sink
: return curr
.onCharSink(dest
, ch
);
273 case EventPhase
.Mine
: return curr
.onChar(ch
);
274 case EventPhase
.Bubble
: return curr
.onCharBubble(dest
, ch
);
276 assert(0); // just in case