1 /* Invisible Vector Library
2 * simple FlexBox-based TUI engine
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 xmain
/*is aliced*/;
25 // ////////////////////////////////////////////////////////////////////////// //
26 __gshared HistoryManager hisman
;
29 // ////////////////////////////////////////////////////////////////////////// //
30 private class FuiHistoryWindow
: FuiWindow
{
31 alias onMyEvent
= super.onMyEvent
;
32 alias onBubbleEvent
= super.onBubbleEvent
;
37 this (FuiEditLine ael
) {
39 this.connectListeners();
45 hlb
= new FuiListBox(this);
46 hlb
.aligning
= hlb
.Align
.Stretch
;
47 foreach_reverse (immutable idx
; 0..hisman
.count(el
)) hlb
.addItem(hisman
.item(el
, idx
));
48 if (hlb
.count
> 1) hlb
.curitem
= 1;
51 hlb
.maxSize
.w
= ttyw
-8;
52 hlb
.maxSize
.h
= ttyh
-8;
54 addEventListener(this, (FuiEventClose evt) {
60 override void onBubbleEvent (FuiEventKey evt
) {
61 if (evt
.key
== "Enter" && hlb
!is null) {
62 auto it
= hlb
.curitem
;
63 if (it
>= 0 && it
< hisman
.count(el
)) {
64 (new FuiEventHistoryReply(el
, hlb
[it
])).post
;
65 //it = hisman.count(el)-it-1;
66 hisman
.activate(el
, it
);
69 super.onBubbleEvent(evt
);
72 static void create (FuiEditLine el
) {
73 if (el
is null) return;
74 auto desk
= el
.getDesk
;
75 if (desk
is null) return;
76 if (hisman
is null) return;
77 if (!hisman
.has(el
) || hisman
.count(el
) < 1) return;
78 auto win
= new FuiHistoryWindow(el
);
80 win
.positionUnderControl(el
);
81 tuidesk
.addPopup(win
);
86 // ////////////////////////////////////////////////////////////////////////// //
87 public class HistoryManager
{
89 enum MaxHistory
= 128;
91 string
[][string
] history
;
94 this () { this.connectListeners(); }
96 void onEvent (FuiEventHistoryQuery evt
) {
97 if (auto el
= cast(FuiEditLine
)evt
.sourcectl
) {
99 FuiHistoryWindow
.create(el
);
103 bool has (FuiControl ctl
) {
104 //if (ctl.id.length == 0) return;
105 //VFile("/home/ketmar/back/D/prj/edgap/zzz", "a").writeln("check: '", id, "'");
106 return ((ctl
.id
in history
) !is null);
109 int count (FuiControl ctl
) {
110 if (auto listp
= ctl
.id
in history
) return listp
.length
;
115 const(char)[] item (FuiControl ctl
, int idx
) {
116 if (idx
< 0) return null;
117 if (auto listp
= ctl
.id
in history
) {
118 return (idx
< listp
.length ?
(*listp
)[idx
] : null);
123 // this can shrink history; should correctly process duplicates
124 void add (FuiControl ctl
, const(char)[] value
) {
125 if (value
.length
== 0 || ctl
.id
.length
== 0) return;
126 if (auto listp
= ctl
.id
in history
) {
127 // check for existing item
128 foreach (immutable idx
; 0..listp
.length
) {
129 if ((*listp
)[idx
] == value
) {
131 activate(ctl
, cast(int)idx
);
135 if (listp
.length
> MaxHistory
) (*listp
).length
= MaxHistory
;
136 if (listp
.length
== MaxHistory
) {
137 // remove oldest item
138 foreach (immutable c
; 1..listp
.length
) (*listp
)[c
-1] = (*listp
)[c
];
139 (*listp
)[$-1] = value
.idup
;
141 (*listp
) ~= value
.idup
;
146 history
[ctl
.id
] = list
;
150 void clear (FuiControl ctl
) {
151 if (auto listp
= ctl
.id
in history
) {
154 (*listp
).assumeSafeAppend
;
159 // usually moves item to bottom
160 void activate (FuiControl ctl
, int idx
) {
162 if (auto listp
= ctl
.id
in history
) {
163 if (listp
.length
> 1 && idx
< listp
.length
-1) {
164 // move item to bottom
165 auto s
= (*listp
)[0];
166 foreach (immutable c
; 1..listp
.length
) (*listp
)[c
-1] = (*listp
)[c
];
174 // ////////////////////////////////////////////////////////////////////////// //
175 public class FuiTextLine
: FuiControl
{
176 alias onMyEvent
= super.onMyEvent
;
178 this (FuiControl aparent
, string atext
) {
179 this.connectListeners();
180 FuiControl ctl
= (aparent
!is null ? aparent
.lastChild
: null);
181 if (ctl
!is null) ctl
.lineBreak
= true;
184 lp
.minSize
.w
= cast(int)atext
.length
;
185 lp
.orientation
= lp
.Orientation
.Horizontal
;
186 lp
.aligning
= lp
.Align
.Stretch
;
187 lp
.minSize
.h
= lp
.maxSize
.h
= 1;
191 protected override void drawSelf (XtWindow win
) {
192 win
.color
= palColor
!"def"();
193 win
.fill(0, 0, win
.width
, win
.height
);
194 win
.writeStrAt(0, 0, caption
);
199 // ////////////////////////////////////////////////////////////////////////// //
200 FuiWindow
createWin (bool closeOnBlur
=false) {
201 import std
.format
: format
;
202 if (hisman
is null) hisman
= new HistoryManager();
203 __gshared
int counter
;
204 auto win
= new FuiWindow();
205 //win.minSize = FuiSize(30, 7);
206 win
.caption
= "Test Window %s".format(counter
++);
207 new FuiTextLine(win
, "hello, i am the first text line");
209 new FuiTextLine(win
, "hello, i am the second text line");
211 if (auto box
= new FuiHBox(win
)) {
212 auto lbl0
= new FuiLabel(box
, "&first:", "bx0");
213 lbl0
.hgroup
= "label0group";
214 if (auto btn
= new FuiButton(box
, "button &0")) { btn
.id
= "bx0"; btn
.lineBreak
= true; }
215 auto lbl1
= new FuiLabel(box
, "&second:", "bx1");
216 lbl1
.hgroup
= "label0group";
217 //if (auto btn = new FuiButton(box, "button &1")) { btn.id = "bx1"; btn.lineBreak = true; }
218 if (auto edt
= new FuiEditLine(box
, "default text")) {
220 edt
.aligning
= edt
.Align
.Stretch
;
222 edt
.lineBreak
= true;
224 if (auto btn
= new FuiCheckBox(box
, "checkbox &2", "cbgroup0")) { btn
.lineBreak
= true; }
225 if (auto pan
= new FuiPanel(box
, "Radio")) {
226 new FuiRadio(pan
, "radio &3", "rbgroup0", 3);
227 new FuiRadio(pan
, "radio &4", "rbgroup0", 4);
228 new FuiRadio(pan
, "radio &5", "rbgroup0", 5);
231 new FuiTextLine(win
, "hello, i am the third text line");
233 if (auto box
= new FuiHBox(win
)) {
235 (new FuiButton(box
, "&OK")).defctl
= true;
236 new FuiButton(box
, "&Cancel");
241 if (auto lb
= new FuiEditor(win
, "line 1\nlist with tab!: \t2\nlast 3")) {
242 //lb.ed.gotoXY(0, 0);
243 lb
.ed
.visualtabs
= true;
245 win
.defaultFocus
= lb
;
248 if (auto lb
= new FuiListBox(win
)) {
251 lb
.allowmarks
= true;
252 foreach (immutable idx
; 0..24) {
253 import std
.format
: format
;
254 lb
.addItem("item #%s".format(idx
));
258 if (closeOnBlur
) win
.onBlur
= (FuiControl self
) { if (auto w
= cast(FuiWindow
)self
) w
.close
; };
261 win
.pos
= FuiPoint((ttyw
-win
.size
.w
)/2, (ttyh
-win
.size
.h
)/2);
263 import std
.random
: uniform
;
264 win
.pos
= FuiPoint(uniform
!"[]"(0, ttyw
-win
.size
.w
), uniform
!"[]"(0, ttyh
-win
.size
.h
));
270 // ////////////////////////////////////////////////////////////////////////// //
271 void main (string
[] args
) {
272 if (ttyIsRedirected
) assert(0, "no redirections, please");
275 auto ttymode
= ttyGetMode();
284 addEventListener((FuiEventQuit evt
) { doQuit
= true; });
286 foreach (immutable _
; 0..2) tuidesk
.addWindow(createWin());
287 tuidesk
.addWindow(createWin(true));
289 tuidesk
.registerHotKey("M-A", () {
290 if (auto ed
= cast(FuiEditLine
)tuidesk
.focused
) {
292 auto str = ed
.getText
!string
;
293 if (str.length
) hisman
.add(ed
, str);
298 foreach (immutable _
; 0..100) {
299 while (ebusSafeDelay
== 0) processEvents();
300 if (ebusSafeDelay
!= 0) break;
304 //{ import core.memory : GC; GC.collect(); GC.minimize(); }
306 if (ttyIsKeyHit || ebusSafeDelay
< 0) {
307 if (ebusSafeDelay
< 0) { import core
.memory
: GC
; GC
.collect(); GC
.minimize(); }
308 auto key
= ttyReadKey(ebusSafeDelay
, TtyDefaultEscWait
);
309 if (key
.key
== TtyEvent
.Key
.Error
) break;
310 if (key
.key
== TtyEvent
.Key
.Unknown
) continue;
311 if (key
== "C-c") break;
312 //if (key.key == TtyEvent.Key.Escape) { (new FuiEventQuit).post; continue; }
313 if (key
.key
== TtyEvent
.Key
.ModChar
&& key
.ctrl
&& !key
.alt
&& !key
.shift
&& key
.ch
== 'L') { xtFullRefresh(); continue; }