egra: cosmetix
[iv.d.git] / egtui / dialogs.d
blob20e64233a98e9839ae1f925acbdb5bdfb3917193
1 /* Invisible Vector Library
2 * simple FlexBox-based TUI engine
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv.egtui.dialogs /*is aliced*/;
18 import iv.alice;
19 import iv.rawtty;
20 import iv.strex;
21 import iv.vfs.io;
23 import iv.egtui.tty;
24 import iv.egtui.tui;
25 import iv.egtui.parser;
28 // ///////////////////////////////////////////////////////////////////////// //
29 void dialogMessage(string type, A...) (const(char)[] title, const(char)[] fmt, A args) {
30 static assert(type == "error" || type == "info" || type == "debug", "invalid message dialog type");
32 enum laydesc = q{
33 caption: "$title"
34 small-frame: false
36 label: { align: expand caption: "$msg" }
38 hline
40 hbox: {
41 hspan
42 button: { id: "btclose" caption: "&Close" }
43 hspan
47 import std.format : format;
48 string msg = fmt.format(args);
50 auto ctx = FuiContext.create();
51 static if (type == "error") {
52 ctx.dialogPalette(TuiPaletteError/*TuiPaletteNormal*/);
54 ctx.parse!(title, msg)(laydesc);
55 ctx.relayout();
56 ctx.modalDialog;
60 // ///////////////////////////////////////////////////////////////////////// //
61 int dialogTanOna (const(char)[] title, const(char)[] text, bool def) {
62 enum laydesc = q{
63 caption: $title
64 small-frame: false
65 // hbox for text
66 hbox: {
67 textview: {
68 id: "text"
69 text: `\C$text`
72 hline
73 // center buttons
74 hbox: {
75 spacing: 1
76 span //: { flex: 1 }
77 button: { id: "bttan" caption: "&tan" }
78 //spacer: { width: 1 } // this hack just inserts space
79 button: { id: "btona" caption: "o&na" }
80 span //: { flex: 1 }
84 auto ctx = FuiContext.create();
85 //ctx.maxDimensions = FuiSize(ttyw, ttyh);
86 ctx.parse!(title, text)(laydesc);
87 ctx.relayout();
88 ctx.focused = ctx[def ? "bttan" : "btona"];
89 auto res = ctx.modalDialog;
90 if (res >= 0) return (ctx.itemId(res) == "bttan" ? 1 : 0);
91 return -1;
95 // ///////////////////////////////////////////////////////////////////////// //
96 // result.ptr is null: calcelled
97 string dialogInputLine(string editorid="") (const(char)[] msg, const(char)[] def=null, FuiHistoryManager dghisman=null) {
98 static if (editorid.length > 0) string editid = editorid; else string editid = "ed";
99 enum laydesc = q{
100 caption: "Input query"
101 small-frame: false
102 // hbox for text
103 label: {
104 align: expand
105 text: `\L$msg:`
107 editline: {
108 align: expand
109 id: "$editid"
110 text: "$def"
112 hline
113 // center buttons
114 hbox: {
115 spacing: 1
116 hspan
117 button: { id: "btok" caption: " O&K " default }
118 button: { id: "btcancel" caption: "&Cancel" }
119 hspan
123 auto ctx = FuiContext.create();
125 ctx.parse!(msg, def, editid)(laydesc);
126 static if (editorid.length > 0) {
127 if (dghisman !is null) ctx.dialogHistoryManager = dghisman;
129 ctx.relayout();
130 if (ctx.layprops(0).position.w < ttyw/3*2) {
131 ctx.layprops(0).minSize.w = ttyw/3*2;
132 ctx.relayout();
134 auto res = ctx.modalDialog;
135 if (res >= 0) {
136 if (auto edl = ctx.firstItemOfType!"editline") {
137 auto ed = edl.ed;
138 char[] rs;
139 auto rng = ed[];
140 if (rng.length == 0) return ""; // so it won't be null
141 rs.reserve(rng.length);
142 foreach (char ch; rng) rs ~= ch;
143 static if (editorid.length > 0) {
144 if (auto hisman = ctx.dialogHistoryManager) hisman.add(editid, rs);
146 return cast(string)rs; // it is safe to cast it here
149 return null;
153 // ///////////////////////////////////////////////////////////////////////// //
154 void dialogTextView (const(char)[] title, const(char)[] text) {
155 enum laydesc = q{
156 caption: "$title"
157 small-frame: false
159 max-width: $winmw
160 max-height: $winmh
161 height: $winmh
163 textview: {
164 id: "text"
165 text: "$text"
166 max-width: $mw
167 //max-height: $mh
168 flex: 1 // eat all possible vertical space
169 can-be-focused
172 hline
174 hbox: {
175 hspan
176 button: { id: "btclose" caption: "&Close" }
177 hspan
181 int winmw = ttyw-8;
182 int winmh = ttyh-4;
183 //winmh = 24;
185 int mw = winmw-6;
186 int mh = winmh-6;
188 auto ctx = FuiContext.create();
189 ctx.parse!(title, text, mw, mh, winmw, winmh)(laydesc);
190 ctx.relayout();
191 int th = ctx.textviewHeight(ctx["text"]);
192 if (th < mh) {
193 // fix size and relayout
194 auto lpr = ctx.layprops(0);
195 lpr.minSize.h = lpr.maxSize.h = lpr.position.h-(mh-th);
196 lpr.position.y = 0; // make sure that `modalDialog()` will center it
197 ctx.relayout();
199 ctx.modalDialog;
203 // ///////////////////////////////////////////////////////////////////////// //
204 // return -1 on escape or index
205 int dialogHistory (FuiHistoryManager hisman, const(char)[] hid, int winx, int winy, int startidx=-1) {
206 if (hisman is null || !hisman.has(hid)) return -1;
208 int hcount = hisman.count(hid);
209 if (hcount < 1) return -1; // just in case
211 int maxhgt = ttyh;
212 if (maxhgt < 3) maxhgt = 3;
214 int maxwdt = ttyw;
215 if (maxwdt < 3) maxwdt = 3;
217 int topline = 0;
218 int maxlen = 0;
219 foreach (int idx; 0..hcount) {
220 auto s = hisman.item(hid, idx);
221 if (s.length > maxlen) maxlen = cast(int)s.length;
223 if (maxlen > ttyw-4) maxlen = ttyw-4;
225 int pgsize = hcount;
226 if (pgsize > maxhgt-2) pgsize = maxhgt-2;
228 if (winx < 0) {
229 winx = (ttyw-(maxlen+4))/2;
230 if (winx < 0) winx = 0;
232 if (winy < 0) {
233 winy = (ttyh-(pgsize+2))/2;
234 if (winy < 0) winy = 0;
237 int x0 = winx;
238 int y0 = winy;
239 // no room to show it at the bottom?
240 if (y0+pgsize+1 > ttyh) {
241 y0 = winy-pgsize-2;
242 // no room to show it at the top? center it then
243 if (y0 < 0 || y0 >= ttyh) y0 = (ttyh-(pgsize+2))/2;
245 if (x0+maxlen+4 > ttyw) x0 = ttyw-maxlen-4;
246 if (x0 < 0) x0 = 0;
247 if (y0 < 0) y0 = 0;
249 int winhgt = pgsize+2;
250 int winwdt = maxlen+4;
252 if (winwdt > maxwdt) winwdt = maxwdt;
254 enum laydesc = q{
255 small-frame: true
256 enter-close: true
257 min-height: $winhgt
258 max-height: $maxhgt
259 min-width: $winwdt
260 max-width: $maxwdt
262 listbox: {
263 id: "lbhistory"
264 flex: 1
265 align: expand
269 auto ctx = FuiContext.create();
270 ctx.parse!(winhgt, winwdt, maxwdt, maxhgt)(laydesc);
272 // add items
273 auto lbi = ctx["lbhistory"];
274 assert(lbi > 0);
275 foreach (int idx; 0..hcount) {
276 auto s = hisman.item(hid, idx);
277 ctx.listboxItemAdd(lbi, s);
279 if (startidx < 0 || startidx >= hcount) {
280 ctx.listboxItemSetCurrent(lbi, hcount-1);
281 } else {
282 ctx.listboxItemSetCurrent(lbi, startidx);
285 ctx.relayout();
286 ctx.layprops(0).position.x = x0;
287 ctx.layprops(0).position.y = y0;
288 auto res = ctx.modalDialog!false; // don't center
289 if (res < 0) return -1;
290 return ctx.listboxItemCurrent(lbi);