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);
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
87 Widget fc
= focusedWidget
;
88 if (fc
is null || fc
is this ||
!fc
.canAcceptFocus()) moveFocusForward();
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 () {
96 Widget fc
= focusedWidget
;
97 foreach (Widget w
; allVisualDepth
) {
99 if (w
.canAcceptFocus()) pf
= w
;
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);
117 foreach (Widget w
; allVisualDepth
) {
121 if (w
.canAcceptFocus()) { nf
= w
; break; }
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
) {
138 if (event
== "Tab" || event
== "C-Tab") { moveFocusForward(); return true; }
139 if (event
== "S-Tab" || event
== "C-S-Tab") { moveFocusBackward(); return true; }
142 immutable bool isDefAccept
= (event
== "Enter");
143 immutable bool isDefCancel
= (event
== "Escape");
147 foreach (Widget w
; allVisualDepth
) {
148 if (w
.isMyHotkey(event
) && w
.hotkeyActivated()) {
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
) {
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
) {
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
;
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
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;
237 immutable GxRect wrect
= curr
.globalRect
;
238 MouseEvent ev
= event
;
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
) {
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