mirror of https://github.com/adamdruppe/arsd.git
minigui updates
This commit is contained in:
parent
239ffedefd
commit
3028278797
96
minigui.d
96
minigui.d
|
@ -31,6 +31,14 @@
|
||||||
FOR BEST RESULTS: be sure to link with the appropriate subsystem command
|
FOR BEST RESULTS: be sure to link with the appropriate subsystem command
|
||||||
`-L/SUBSYSTEM:WINDOWS:5.0`, for example, because otherwise you'll get a
|
`-L/SUBSYSTEM:WINDOWS:5.0`, for example, because otherwise you'll get a
|
||||||
console and other visual bugs.
|
console and other visual bugs.
|
||||||
|
|
||||||
|
HTML_To_Classes:
|
||||||
|
`<input type="text">` = [LineEdit]
|
||||||
|
`<textarea>` = [TextEdit]
|
||||||
|
`<select>` = [DropDownSelection]
|
||||||
|
`<input type="checkbox">` = [Checkbox]
|
||||||
|
`<input type="radio">` = [Radiobox]
|
||||||
|
`<button>` = [Button]
|
||||||
+/
|
+/
|
||||||
module arsd.minigui;
|
module arsd.minigui;
|
||||||
|
|
||||||
|
@ -82,11 +90,15 @@ abstract class ComboboxBase : Widget {
|
||||||
// if the user can enter arbitrary data, we want to use 2 == CBS_DROPDOWN
|
// if the user can enter arbitrary data, we want to use 2 == CBS_DROPDOWN
|
||||||
// or to always show the list, we want CBS_SIMPLE == 1
|
// or to always show the list, we want CBS_SIMPLE == 1
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
this(uint style, Widget parent = null) {
|
this(uint style, Widget parent = null) {
|
||||||
super(parent);
|
super(parent);
|
||||||
parentWindow = parent.parentWindow;
|
parentWindow = parent.parentWindow;
|
||||||
createWin32Window(this, "ComboBox", null, style);
|
createWin32Window(this, "ComboBox", null, style);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
this(Widget parent = null) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
private string[] options;
|
private string[] options;
|
||||||
private int selection = -1;
|
private int selection = -1;
|
||||||
|
@ -109,30 +121,51 @@ abstract class ComboboxBase : Widget {
|
||||||
auto event = new Event("changed", this);
|
auto event = new Event("changed", this);
|
||||||
event.dispatch();
|
event.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int minHeight() { return Window.lineHeight * 4 / 3; }
|
||||||
|
override int maxHeight() { return Window.lineHeight * 4 / 3; }
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/++
|
||||||
|
A drop-down list where the user must select one of the
|
||||||
|
given options. Like `<select>` in HTML.
|
||||||
|
+/
|
||||||
class DropDownSelection : ComboboxBase {
|
class DropDownSelection : ComboboxBase {
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
super(3 /* CBS_DROPDOWNLIST */, parent);
|
super(3 /* CBS_DROPDOWNLIST */, parent);
|
||||||
|
else
|
||||||
|
super(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/++
|
||||||
|
A text box with a drop down arrow listing selections.
|
||||||
|
The user can choose from the list, or type their own.
|
||||||
|
+/
|
||||||
class FreeEntrySelection : ComboboxBase {
|
class FreeEntrySelection : ComboboxBase {
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
super(2 /* CBS_DROPDOWN */, parent);
|
super(2 /* CBS_DROPDOWN */, parent);
|
||||||
|
else
|
||||||
|
super(parent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
/++
|
||||||
|
A combination of free entry with a list below it.
|
||||||
|
+/
|
||||||
class ComboBox : ComboboxBase {
|
class ComboBox : ComboboxBase {
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
super(1 /* CBS_SIMPLE */, parent);
|
super(1 /* CBS_SIMPLE */, parent);
|
||||||
|
else
|
||||||
|
super(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int minHeight() { return Window.lineHeight * 3; }
|
||||||
|
override int maxHeight() { return int.max; }
|
||||||
|
override int heightStretchiness() { return 2; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -990,10 +1023,12 @@ class Window : Widget {
|
||||||
SimpleWindow win;
|
SimpleWindow win;
|
||||||
|
|
||||||
this(Widget p) {
|
this(Widget p) {
|
||||||
|
tabStop = false;
|
||||||
super(p);
|
super(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
this(SimpleWindow win) {
|
this(SimpleWindow win) {
|
||||||
|
tabStop = false;
|
||||||
super(null);
|
super(null);
|
||||||
this.win = win;
|
this.win = win;
|
||||||
|
|
||||||
|
@ -1260,10 +1295,23 @@ class Window : Widget {
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
recomputeChildLayout();
|
recomputeChildLayout();
|
||||||
|
focusedWidget = getFirstFocusable(this); // FIXME: autofocus?
|
||||||
win.show();
|
win.show();
|
||||||
redraw();
|
redraw();
|
||||||
win.eventLoop(0);
|
win.eventLoop(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Widget getFirstFocusable(Widget start) {
|
||||||
|
if(start.tabStop)
|
||||||
|
return start;
|
||||||
|
|
||||||
|
foreach(child; start.children) {
|
||||||
|
auto f = getFirstFocusable(child);
|
||||||
|
if(f !is null)
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -1449,9 +1497,11 @@ class ToolBar : Widget {
|
||||||
class ToolButton : Button {
|
class ToolButton : Button {
|
||||||
this(string label, Widget parent = null) {
|
this(string label, Widget parent = null) {
|
||||||
super(label, parent);
|
super(label, parent);
|
||||||
|
tabStop = false;
|
||||||
}
|
}
|
||||||
this(Action action, Widget parent = null) {
|
this(Action action, Widget parent = null) {
|
||||||
super(action.label, parent);
|
super(action.label, parent);
|
||||||
|
tabStop = false;
|
||||||
this.action = action;
|
this.action = action;
|
||||||
|
|
||||||
version(win32_widgets) {} else {
|
version(win32_widgets) {} else {
|
||||||
|
@ -1485,9 +1535,11 @@ class MenuBar : Widget {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
handle = CreateMenu();
|
handle = CreateMenu();
|
||||||
|
tabStop = false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
|
tabStop = false; // these are selected some other way
|
||||||
super(parent);
|
super(parent);
|
||||||
this.paint = (ScreenPainter painter) {
|
this.paint = (ScreenPainter painter) {
|
||||||
draw3dFrame(this, painter, FrameStyle.risen);
|
draw3dFrame(this, painter, FrameStyle.risen);
|
||||||
|
@ -1681,6 +1733,7 @@ class ProgressBar : Widget {
|
||||||
super(parent);
|
super(parent);
|
||||||
max = 100;
|
max = 100;
|
||||||
step = 10;
|
step = 10;
|
||||||
|
tabStop = false;
|
||||||
paint = (ScreenPainter painter) {
|
paint = (ScreenPainter painter) {
|
||||||
this.draw3dFrame(painter, FrameStyle.sunk);
|
this.draw3dFrame(painter, FrameStyle.sunk);
|
||||||
painter.fillColor = Color.blue;
|
painter.fillColor = Color.blue;
|
||||||
|
@ -1940,13 +1993,14 @@ class MenuItem : MouseActivatedWidget {
|
||||||
painter.fillColor = Color.transparent;
|
painter.fillColor = Color.transparent;
|
||||||
painter.drawText(Point(cast(MenuBar) this.parent ? 4 : 20, 2), label, Point(width, height), TextAlignment.Left);
|
painter.drawText(Point(cast(MenuBar) this.parent ? 4 : 20, 2), label, Point(width, height), TextAlignment.Left);
|
||||||
};
|
};
|
||||||
|
tabStop = false; // these are selected some other way
|
||||||
}
|
}
|
||||||
|
|
||||||
this(Action action, Widget parent = null) {
|
this(Action action, Widget parent = null) {
|
||||||
assert(action !is null);
|
assert(action !is null);
|
||||||
this(action.label);
|
this(action.label);
|
||||||
this.action = action;
|
this.action = action;
|
||||||
defaultEventHandlers["click"] = (Widget w, Event ev) {
|
defaultEventHandlers["triggered"] = (Widget w, Event ev) {
|
||||||
//auto event = new Event("triggered", this);
|
//auto event = new Event("triggered", this);
|
||||||
//event.dispatch();
|
//event.dispatch();
|
||||||
foreach(handler; action.triggered)
|
foreach(handler; action.triggered)
|
||||||
|
@ -1955,6 +2009,7 @@ class MenuItem : MouseActivatedWidget {
|
||||||
if(auto pmenu = cast(Menu) this.parent)
|
if(auto pmenu = cast(Menu) this.parent)
|
||||||
pmenu.remove();
|
pmenu.remove();
|
||||||
};
|
};
|
||||||
|
tabStop = false; // these are selected some other way
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2286,6 +2341,9 @@ class TextLabel : Widget {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version(custom_widgets)
|
||||||
|
mixin ExperimentalTextComponent;
|
||||||
|
|
||||||
/// Contains the implementation of text editing
|
/// Contains the implementation of text editing
|
||||||
abstract class EditableTextWidget : Widget {
|
abstract class EditableTextWidget : Widget {
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
|
@ -2315,6 +2373,9 @@ abstract class EditableTextWidget : Widget {
|
||||||
else {
|
else {
|
||||||
textLayout.clear();
|
textLayout.clear();
|
||||||
textLayout.addText(s);
|
textLayout.addText(s);
|
||||||
|
textLayout.addText(ForegroundColor.red, s);
|
||||||
|
textLayout.addText(ForegroundColor.blue, TextFormat.underline, "http://dpldocs.info/");
|
||||||
|
textLayout.addText(" is the best!");
|
||||||
redraw();
|
redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2322,7 +2383,6 @@ abstract class EditableTextWidget : Widget {
|
||||||
version(win32_widgets) { /* will do it with Windows calls in the classes */ }
|
version(win32_widgets) { /* will do it with Windows calls in the classes */ }
|
||||||
else {
|
else {
|
||||||
// FIXME
|
// FIXME
|
||||||
mixin ExperimentalTextComponent;
|
|
||||||
|
|
||||||
Timer caratTimer;
|
Timer caratTimer;
|
||||||
TextLayout textLayout;
|
TextLayout textLayout;
|
||||||
|
@ -2448,9 +2508,13 @@ class LineEdit : EditableTextWidget {
|
||||||
0, WS_EX_CLIENTEDGE);//|WS_HSCROLL|ES_AUTOHSCROLL);
|
0, WS_EX_CLIENTEDGE);//|WS_HSCROLL|ES_AUTOHSCROLL);
|
||||||
} else {
|
} else {
|
||||||
setupCustomTextEditing();
|
setupCustomTextEditing();
|
||||||
|
addEventListener("char", delegate(Widget _this, Event ev) {
|
||||||
|
if(ev.character == '\n')
|
||||||
|
ev.preventDefault();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override int maxHeight() { return Window.lineHeight + 0; }
|
override int maxHeight() { return Window.lineHeight + 4; }
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -2466,7 +2530,7 @@ class TextEdit : EditableTextWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override int maxHeight() { return int.max; }
|
override int maxHeight() { return int.max; }
|
||||||
override int heightStretchiness() { return 3; }
|
override int heightStretchiness() { return 4; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2488,7 +2552,7 @@ class MessageBox : Window {
|
||||||
button. x = this.width / 2 - button.width / 2;
|
button. x = this.width / 2 - button.width / 2;
|
||||||
button.y = height - (button.height + 10);
|
button.y = height - (button.height + 10);
|
||||||
button.addEventListener(EventType.triggered, () {
|
button.addEventListener(EventType.triggered, () {
|
||||||
close();
|
win.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
button.registerMovement();
|
button.registerMovement();
|
||||||
|
|
|
@ -1418,7 +1418,32 @@ class SimpleWindow : CapableOfHandlingNativeEvent {
|
||||||
T eventHandlers) /// delegate list like std.concurrency.receive
|
T eventHandlers) /// delegate list like std.concurrency.receive
|
||||||
{
|
{
|
||||||
setEventHandlers(eventHandlers);
|
setEventHandlers(eventHandlers);
|
||||||
return impl.eventLoop(pulseTimeout);
|
version(X11) {
|
||||||
|
keep_trying:
|
||||||
|
try {
|
||||||
|
return impl.eventLoop(pulseTimeout);
|
||||||
|
} catch(XDisconnectException e) {
|
||||||
|
if(e.userRequested)
|
||||||
|
XCloseDisplay(XDisplayConnection.display);
|
||||||
|
|
||||||
|
XDisplayConnection.display = null;
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
|
||||||
|
/*
|
||||||
|
auto dpy = XDisplayConnection.get;
|
||||||
|
if(dpy is null)
|
||||||
|
throw new XDisconnectException(false);
|
||||||
|
foreach(Window w, SimpleWindow window; SimpleWindow.nativeMapping) {
|
||||||
|
window.impl.createWindow(window.width, window.height, window.title, OpenGlOptions.no, null); //, window.opengl, window.parent);
|
||||||
|
window.show();
|
||||||
|
}
|
||||||
|
goto keep_trying;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return impl.eventLoop(pulseTimeout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/++
|
/++
|
||||||
|
@ -6305,6 +6330,13 @@ version(X11) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class XDisconnectException : Exception {
|
||||||
|
bool userRequested;
|
||||||
|
this(bool userRequested = true) {
|
||||||
|
this.userRequested = userRequested;
|
||||||
|
super("X disconnected");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Platform-specific for X11. A singleton class (well, all its methods are actually static... so more like a namespace) wrapping a Display*
|
/// Platform-specific for X11. A singleton class (well, all its methods are actually static... so more like a namespace) wrapping a Display*
|
||||||
class XDisplayConnection {
|
class XDisplayConnection {
|
||||||
|
@ -6396,6 +6428,7 @@ version(X11) {
|
||||||
display = XOpenDisplay(null);
|
display = XOpenDisplay(null);
|
||||||
if(display is null)
|
if(display is null)
|
||||||
throw new Exception("Unable to open X display");
|
throw new Exception("Unable to open X display");
|
||||||
|
XSetIOErrorHandler(&x11ioerrCB);
|
||||||
Bool sup;
|
Bool sup;
|
||||||
XkbSetDetectableAutoRepeat(display, 1, &sup); // so we will not receive KeyRelease until key is really released
|
XkbSetDetectableAutoRepeat(display, 1, &sup); // so we will not receive KeyRelease until key is really released
|
||||||
createXIM();
|
createXIM();
|
||||||
|
@ -6408,6 +6441,11 @@ version(X11) {
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern(C)
|
||||||
|
static int x11ioerrCB(Display* dpy) {
|
||||||
|
throw new XDisconnectException(false);
|
||||||
|
}
|
||||||
|
|
||||||
version(with_eventloop) {
|
version(with_eventloop) {
|
||||||
import arsd.eventloop;
|
import arsd.eventloop;
|
||||||
static void eventListener(OsFileHandle fd) {
|
static void eventListener(OsFileHandle fd) {
|
||||||
|
@ -9099,6 +9137,12 @@ alias void* GLXContext;
|
||||||
|
|
||||||
enum AllocNone = 0;
|
enum AllocNone = 0;
|
||||||
|
|
||||||
|
extern(C) {
|
||||||
|
/* WARNING, this type not in Xlib spec */
|
||||||
|
extern(C) alias XIOErrorHandler = int function (Display* display);
|
||||||
|
XIOErrorHandler XSetIOErrorHandler (XIOErrorHandler handler);
|
||||||
|
}
|
||||||
|
|
||||||
extern(C) nothrow @nogc {
|
extern(C) nothrow @nogc {
|
||||||
struct Screen{
|
struct Screen{
|
||||||
XExtData *ext_data; /* hook for extension to hang data */
|
XExtData *ext_data; /* hook for extension to hang data */
|
||||||
|
@ -9356,10 +9400,6 @@ struct Visual
|
||||||
|
|
||||||
int XGetErrorText(Display*, int, char*, int);
|
int XGetErrorText(Display*, int, char*, int);
|
||||||
|
|
||||||
/* WARNING, this type not in Xlib spec */
|
|
||||||
extern(C) alias XIOErrorHandler = int function (Display* display);
|
|
||||||
XIOErrorHandler XSetIOErrorHandler (XIOErrorHandler handler);
|
|
||||||
|
|
||||||
Bool XkbSetDetectableAutoRepeat(Display* dpy, Bool detectable, Bool* supported);
|
Bool XkbSetDetectableAutoRepeat(Display* dpy, Bool detectable, Bool* supported);
|
||||||
|
|
||||||
|
|
||||||
|
@ -10734,7 +10774,7 @@ mixin template ExperimentalTextComponent() {
|
||||||
|
|
||||||
painter.drawText(pos, part.text);
|
painter.drawText(pos, part.text);
|
||||||
if(part.styles & TextFormat.underline)
|
if(part.styles & TextFormat.underline)
|
||||||
painter.drawLine(Point(pos.x, pos.y + size.height - 1), Point(pos.x + size.width, pos.y + size.height - 1));
|
painter.drawLine(Point(pos.x, pos.y + size.height - 4), Point(pos.x + size.width, pos.y + size.height - 4));
|
||||||
if(part.styles & TextFormat.strikethrough)
|
if(part.styles & TextFormat.strikethrough)
|
||||||
painter.drawLine(Point(pos.x, pos.y + size.height/2), Point(pos.x + size.width, pos.y + size.height/2));
|
painter.drawLine(Point(pos.x, pos.y + size.height/2), Point(pos.x + size.width, pos.y + size.height/2));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue