mirror of https://github.com/adamdruppe/arsd.git
font api in sdpy
This commit is contained in:
parent
45e4fc98ec
commit
df9c912bd9
4
color.d
4
color.d
|
@ -1256,6 +1256,10 @@ struct Point {
|
||||||
Point opBinary(string op)(Point rhs) {
|
Point opBinary(string op)(Point rhs) {
|
||||||
return Point(mixin("x" ~ op ~ "rhs.x"), mixin("y" ~ op ~ "rhs.y"));
|
return Point(mixin("x" ~ op ~ "rhs.x"), mixin("y" ~ op ~ "rhs.y"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point opBinary(string op)(int rhs) {
|
||||||
|
return Point(mixin("x" ~ op ~ "rhs"), mixin("y" ~ op ~ "rhs"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
270
minigui.d
270
minigui.d
|
@ -1,5 +1,18 @@
|
||||||
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775498%28v=vs.85%29.aspx
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/bb775498%28v=vs.85%29.aspx
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO:
|
||||||
|
|
||||||
|
scrolling
|
||||||
|
event cleanup
|
||||||
|
ScreenPainter dtor stuff. clipping api.
|
||||||
|
Windows radio button sizing and theme text selection
|
||||||
|
tooltips.
|
||||||
|
api improvements
|
||||||
|
|
||||||
|
margins are kinda broken, they don't collapse like they should. at least.
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
1(15:19:48) NotSpooky: Menus, text entry, label, notebook, box, frame, file dialogs and layout (this one is very useful because I can draw lines between its child widgets
|
1(15:19:48) NotSpooky: Menus, text entry, label, notebook, box, frame, file dialogs and layout (this one is very useful because I can draw lines between its child widgets
|
||||||
|
@ -123,6 +136,27 @@ abstract class ComboboxBase : Widget {
|
||||||
else version(custom_widgets)
|
else version(custom_widgets)
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
|
addEventListener("keydown", (Event event) {
|
||||||
|
if(event.key == Key.Up) {
|
||||||
|
if(selection > -1) { // -1 means select blank
|
||||||
|
selection--;
|
||||||
|
auto t = new Event("change", this);
|
||||||
|
t.dispatch();
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
if(event.key == Key.Down) {
|
||||||
|
if(selection + 1 < options.length) {
|
||||||
|
selection++;
|
||||||
|
auto t = new Event("change", this);
|
||||||
|
t.dispatch();
|
||||||
|
}
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
else static assert(false);
|
else static assert(false);
|
||||||
|
|
||||||
|
@ -139,12 +173,15 @@ abstract class ComboboxBase : Widget {
|
||||||
selection = idx;
|
selection = idx;
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
SendMessageA(hwnd, 334 /*CB_SETCURSEL*/, idx, 0);
|
SendMessageA(hwnd, 334 /*CB_SETCURSEL*/, idx, 0);
|
||||||
|
|
||||||
|
auto t = new Event("change", this);
|
||||||
|
t.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
override void handleWmCommand(ushort cmd, ushort id) {
|
override void handleWmCommand(ushort cmd, ushort id) {
|
||||||
selection = SendMessageA(hwnd, 327 /* CB_GETCURSEL */, 0, 0);
|
selection = SendMessageA(hwnd, 327 /* CB_GETCURSEL */, 0, 0);
|
||||||
auto event = new Event("changed", this);
|
auto event = new Event("change", this);
|
||||||
event.dispatch();
|
event.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,12 +214,12 @@ abstract class ComboboxBase : Widget {
|
||||||
|
|
||||||
dropDown.setEventHandlers(
|
dropDown.setEventHandlers(
|
||||||
(MouseEvent event) {
|
(MouseEvent event) {
|
||||||
if(event.type == MouseEventType.buttonPressed) {
|
if(event.type == MouseEventType.buttonReleased) {
|
||||||
auto element = (event.y - 4) / Window.lineHeight;
|
auto element = (event.y - 4) / Window.lineHeight;
|
||||||
if(element >= 0 && element <= options.length) {
|
if(element >= 0 && element <= options.length) {
|
||||||
selection = element;
|
selection = element;
|
||||||
|
|
||||||
auto t = new Event("changed", this);
|
auto t = new Event("change", this);
|
||||||
t.dispatch();
|
t.dispatch();
|
||||||
}
|
}
|
||||||
dropDown.close();
|
dropDown.close();
|
||||||
|
@ -211,12 +248,36 @@ class DropDownSelection : ComboboxBase {
|
||||||
draw3dFrame(this, painter, FrameStyle.risen);
|
draw3dFrame(this, painter, FrameStyle.risen);
|
||||||
painter.outlineColor = Color.black;
|
painter.outlineColor = Color.black;
|
||||||
painter.drawText(Point(4, 4), selection == -1 ? "" : options[selection]);
|
painter.drawText(Point(4, 4), selection == -1 ? "" : options[selection]);
|
||||||
painter.drawLine(Point(width - 4 - 8, 4), Point(width - 4 - 2, height - 2));
|
|
||||||
painter.drawLine(Point(width - 2, 4), Point(width - 4 - 2, height - 2));
|
painter.outlineColor = Color.black;
|
||||||
|
painter.fillColor = Color.black;
|
||||||
|
Point[3] triangle;
|
||||||
|
enum padding = 6;
|
||||||
|
enum paddingV = 8;
|
||||||
|
enum triangleWidth = 10;
|
||||||
|
triangle[0] = Point(width - padding - triangleWidth, paddingV);
|
||||||
|
triangle[1] = Point(width - padding - triangleWidth / 2, height - paddingV);
|
||||||
|
triangle[2] = Point(width - padding - 0, paddingV);
|
||||||
|
painter.drawPolygon(triangle[]);
|
||||||
|
|
||||||
|
if(isFocused()) {
|
||||||
|
painter.fillColor = Color.transparent;
|
||||||
|
painter.pen = Pen(Color.black, 1, Pen.Style.Dashed);
|
||||||
|
painter.drawRectangle(Point(2, 2), width - 4, height - 4);
|
||||||
|
painter.pen = Pen(Color.black, 1, Pen.Style.Solid);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
addEventListener("changed", &this.redraw);
|
addEventListener("focus", &this.redraw);
|
||||||
addEventListener("click", &this.popup);
|
addEventListener("blur", &this.redraw);
|
||||||
|
addEventListener("change", &this.redraw);
|
||||||
|
addEventListener("mousedown", () { this.focus(); this.popup(); });
|
||||||
|
addEventListener("keydown", (Event event) {
|
||||||
|
if(event.key == Key.Space)
|
||||||
|
popup();
|
||||||
|
});
|
||||||
} else static assert(false);
|
} else static assert(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,7 +294,8 @@ class FreeEntrySelection : ComboboxBase {
|
||||||
super(parent);
|
super(parent);
|
||||||
auto hl = new HorizontalLayout(this);
|
auto hl = new HorizontalLayout(this);
|
||||||
lineEdit = new LineEdit(hl);
|
lineEdit = new LineEdit(hl);
|
||||||
lineEdit.content = selection == -1 ? "" : options[selection];
|
|
||||||
|
tabStop = false;
|
||||||
|
|
||||||
auto btn = new class Button {
|
auto btn = new class Button {
|
||||||
this() {
|
this() {
|
||||||
|
@ -243,9 +305,11 @@ class FreeEntrySelection : ComboboxBase {
|
||||||
return 16;
|
return 16;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
//btn.addDirectEventListener("focus", &lineEdit.focus);
|
||||||
btn.addEventListener("triggered", &this.popup);
|
btn.addEventListener("triggered", &this.popup);
|
||||||
addEventListener("changed", {
|
addEventListener("change", {
|
||||||
lineEdit.content = selection == -1 ? "" : options[selection];
|
lineEdit.content = (selection == -1 ? "" : options[selection]);
|
||||||
|
lineEdit.focus();
|
||||||
redraw();
|
redraw();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -278,6 +342,30 @@ class ComboBox : ComboboxBase {
|
||||||
}
|
}
|
||||||
lineEdit.content = c;
|
lineEdit.content = c;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
listWidget.tabStop = false;
|
||||||
|
this.tabStop = false;
|
||||||
|
listWidget.addEventListener("focus", &lineEdit.focus);
|
||||||
|
this.addEventListener("focus", &lineEdit.focus);
|
||||||
|
|
||||||
|
addDirectEventListener("change", {
|
||||||
|
listWidget.setSelection(selection);
|
||||||
|
if(selection != -1)
|
||||||
|
lineEdit.content = options[selection];
|
||||||
|
lineEdit.focus();
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
|
||||||
|
listWidget.addDirectEventListener("change", {
|
||||||
|
int set = -1;
|
||||||
|
foreach(idx, opt; listWidget.options)
|
||||||
|
if(opt.selected) {
|
||||||
|
set = cast(int) idx;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(set != selection)
|
||||||
|
this.setSelection(set);
|
||||||
|
});
|
||||||
} else static assert(false);
|
} else static assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,21 +395,28 @@ class ListWidget : Widget {
|
||||||
bool selected;
|
bool selected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setSelection(int y) {
|
||||||
|
if(!multiSelect)
|
||||||
|
foreach(ref opt; options)
|
||||||
|
opt.selected = false;
|
||||||
|
if(y >= 0 && y < options.length)
|
||||||
|
options[y].selected = !options[y].selected;
|
||||||
|
|
||||||
|
auto evt = new Event("change", this);
|
||||||
|
evt.dispatch();
|
||||||
|
|
||||||
|
redraw();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
super(parent);
|
super(parent);
|
||||||
|
|
||||||
defaultEventHandlers["click"] = delegate(Widget _this, Event event) {
|
defaultEventHandlers["click"] = delegate(Widget _this, Event event) {
|
||||||
|
this.focus();
|
||||||
auto y = (event.clientY - 4) / Window.lineHeight;
|
auto y = (event.clientY - 4) / Window.lineHeight;
|
||||||
if(y >= 0 && y < options.length) {
|
if(y >= 0 && y < options.length) {
|
||||||
if(!multiSelect)
|
setSelection(y);
|
||||||
foreach(ref opt; options)
|
|
||||||
opt.selected = false;
|
|
||||||
options[y].selected = !options[y].selected;
|
|
||||||
|
|
||||||
auto evt = new Event("change", this);
|
|
||||||
evt.dispatch();
|
|
||||||
|
|
||||||
redraw();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -588,6 +683,7 @@ mixin template LayoutInfo() {
|
||||||
foreach(child; children) {
|
foreach(child; children) {
|
||||||
sum += child.minHeight();
|
sum += child.minHeight();
|
||||||
sum += child.marginTop();
|
sum += child.marginTop();
|
||||||
|
sum += child.marginBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
return sum;
|
return sum;
|
||||||
|
@ -806,6 +902,19 @@ version(win32_widgets) {
|
||||||
|
|
||||||
assert(p.hwnd !is null);
|
assert(p.hwnd !is null);
|
||||||
|
|
||||||
|
|
||||||
|
static HFONT font;
|
||||||
|
if(font is null) {
|
||||||
|
NONCLIENTMETRICS params;
|
||||||
|
params.cbSize = params.sizeof;
|
||||||
|
if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, params.sizeof, ¶ms, 0)) {
|
||||||
|
font = CreateFontIndirect(¶ms.lfMessageFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(font)
|
||||||
|
SendMessage(p.hwnd, WM_SETFONT, cast(uint) font, true);
|
||||||
|
|
||||||
Widget.nativeMapping[p.hwnd] = p;
|
Widget.nativeMapping[p.hwnd] = p;
|
||||||
|
|
||||||
p.originalWindowProcedure = cast(WNDPROC) SetWindowLong(p.hwnd, GWL_WNDPROC, cast(LONG) &HookedWndProc);
|
p.originalWindowProcedure = cast(WNDPROC) SetWindowLong(p.hwnd, GWL_WNDPROC, cast(LONG) &HookedWndProc);
|
||||||
|
@ -993,7 +1102,7 @@ class Widget {
|
||||||
|
|
||||||
parentWindow.focusedWidget = this;
|
parentWindow.focusedWidget = this;
|
||||||
auto evt = new Event("focus", this);
|
auto evt = new Event("focus", this);
|
||||||
evt.sendDirectly();
|
evt.dispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1206,10 +1315,10 @@ class Window : Widget {
|
||||||
win.onFocusChange = (bool getting) {
|
win.onFocusChange = (bool getting) {
|
||||||
if(this.focusedWidget) {
|
if(this.focusedWidget) {
|
||||||
auto evt = new Event(getting ? "focus" : "blur", this.focusedWidget);
|
auto evt = new Event(getting ? "focus" : "blur", this.focusedWidget);
|
||||||
evt.sendDirectly();
|
evt.dispatch();
|
||||||
}
|
}
|
||||||
auto evt = new Event(getting ? "focus" : "blur", this);
|
auto evt = new Event(getting ? "focus" : "blur", this);
|
||||||
evt.sendDirectly();
|
evt.dispatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
win.setEventHandlers(
|
win.setEventHandlers(
|
||||||
|
@ -1606,8 +1715,8 @@ class ToolBar : Widget {
|
||||||
override int minHeight() { return idealHeight; }
|
override int minHeight() { return idealHeight; }
|
||||||
override int maxHeight() { return idealHeight; }
|
override int maxHeight() { return idealHeight; }
|
||||||
} else version(custom_widgets) {
|
} else version(custom_widgets) {
|
||||||
override int minHeight() { return Window.lineHeight * 3/2; }
|
override int minHeight() { return 32; }// Window.lineHeight * 3/2; }
|
||||||
override int maxHeight() { return Window.lineHeight * 3/2; }
|
override int maxHeight() { return 32; } //Window.lineHeight * 3/2; }
|
||||||
} else static assert(false);
|
} else static assert(false);
|
||||||
override int heightStretchiness() { return 0; }
|
override int heightStretchiness() { return 0; }
|
||||||
|
|
||||||
|
@ -1685,7 +1794,61 @@ class ToolButton : Button {
|
||||||
this.draw3dFrame(painter, isDepressed ? FrameStyle.sunk : FrameStyle.risen, currentButtonColor);
|
this.draw3dFrame(painter, isDepressed ? FrameStyle.sunk : FrameStyle.risen, currentButtonColor);
|
||||||
|
|
||||||
painter.outlineColor = Color.black;
|
painter.outlineColor = Color.black;
|
||||||
painter.drawText(Point(0, 0), action.label, Point(width, height), TextAlignment.Center | TextAlignment.VerticalCenter);
|
|
||||||
|
enum iconSize = 32;
|
||||||
|
enum multiplier = iconSize / 16;
|
||||||
|
switch(action.iconId) {
|
||||||
|
case GenericIcons.New:
|
||||||
|
painter.fillColor = Color.white;
|
||||||
|
painter.drawPolygon(
|
||||||
|
Point(3, 2) * multiplier, Point(3, 13) * multiplier, Point(12, 13) * multiplier, Point(12, 6) * multiplier,
|
||||||
|
Point(8, 2) * multiplier, Point(8, 6) * multiplier, Point(12, 6) * multiplier, Point(8, 2) * multiplier
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case GenericIcons.Save:
|
||||||
|
painter.fillColor = Color.black;
|
||||||
|
painter.drawRectangle(Point(2, 2) * multiplier, Point(13, 13) * multiplier);
|
||||||
|
|
||||||
|
painter.fillColor = Color.white;
|
||||||
|
painter.outlineColor = Color.white;
|
||||||
|
painter.drawRectangle(Point(6, 3) * multiplier, Point(9, 5) * multiplier);
|
||||||
|
painter.drawRectangle(Point(5, 9) * multiplier, Point(10, 12) * multiplier);
|
||||||
|
break;
|
||||||
|
case GenericIcons.Open:
|
||||||
|
painter.fillColor = Color.white;
|
||||||
|
painter.drawPolygon(
|
||||||
|
Point(2, 4) * multiplier, Point(2, 12) * multiplier, Point(13, 12) * multiplier, Point(13, 3) * multiplier,
|
||||||
|
Point(9, 3) * multiplier, Point(9, 4) * multiplier, Point(2, 4) * multiplier);
|
||||||
|
painter.drawLine(Point(3, 6) * multiplier, Point(9, 6) * multiplier);
|
||||||
|
painter.drawLine(Point(9, 7) * multiplier, Point(13, 7) * multiplier);
|
||||||
|
break;
|
||||||
|
case GenericIcons.Copy:
|
||||||
|
painter.fillColor = Color.white;
|
||||||
|
painter.drawRectangle(Point(3, 2) * multiplier, Point(9, 10) * multiplier);
|
||||||
|
painter.drawRectangle(Point(6, 5) * multiplier, Point(12, 13) * multiplier);
|
||||||
|
break;
|
||||||
|
case GenericIcons.Cut:
|
||||||
|
painter.fillColor = Color.transparent;
|
||||||
|
painter.drawLine(Point(3, 2) * multiplier, Point(10, 9) * multiplier);
|
||||||
|
painter.drawLine(Point(4, 9) * multiplier, Point(11, 2) * multiplier);
|
||||||
|
painter.drawRectangle(Point(3, 9) * multiplier, Point(5, 13) * multiplier);
|
||||||
|
painter.drawRectangle(Point(9, 9) * multiplier, Point(11, 12) * multiplier);
|
||||||
|
break;
|
||||||
|
case GenericIcons.Paste:
|
||||||
|
painter.fillColor = Color.white;
|
||||||
|
painter.drawRectangle(Point(2, 3) * multiplier, Point(11, 11) * multiplier);
|
||||||
|
painter.drawRectangle(Point(6, 8) * multiplier, Point(13, 13) * multiplier);
|
||||||
|
painter.drawLine(Point(6, 2) * multiplier, Point(4, 5) * multiplier);
|
||||||
|
painter.drawLine(Point(7, 2) * multiplier, Point(9, 5) * multiplier);
|
||||||
|
painter.fillColor = Color.black;
|
||||||
|
painter.drawRectangle(Point(4, 5) * multiplier, Point(9, 6) * multiplier);
|
||||||
|
break;
|
||||||
|
case GenericIcons.Help:
|
||||||
|
painter.drawText(Point(0, 0), "?", Point(width, height), TextAlignment.Center | TextAlignment.VerticalCenter);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
painter.drawText(Point(0, 0), action.label, Point(width, height), TextAlignment.Center | TextAlignment.VerticalCenter);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else static assert(false);
|
else static assert(false);
|
||||||
|
@ -1693,7 +1856,10 @@ class ToolButton : Button {
|
||||||
|
|
||||||
Action action;
|
Action action;
|
||||||
|
|
||||||
override int maxWidth() { return 40; }
|
override int maxWidth() { return 32; }
|
||||||
|
override int minWidth() { return 32; }
|
||||||
|
override int maxHeight() { return 32; }
|
||||||
|
override int minHeight() { return 32; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1854,7 +2020,7 @@ class StatusBar : Widget {
|
||||||
assert(idealHeight);
|
assert(idealHeight);
|
||||||
} else version(custom_widgets) {
|
} else version(custom_widgets) {
|
||||||
this.paint = (ScreenPainter painter) {
|
this.paint = (ScreenPainter painter) {
|
||||||
this.draw3dFrame(painter, FrameStyle.risen);
|
this.draw3dFrame(painter, FrameStyle.sunk);
|
||||||
int cpos = 0;
|
int cpos = 0;
|
||||||
int remainingLength = this.width;
|
int remainingLength = this.width;
|
||||||
foreach(idx, part; this.partsArray) {
|
foreach(idx, part; this.partsArray) {
|
||||||
|
@ -2287,9 +2453,15 @@ else static assert(false);
|
||||||
///
|
///
|
||||||
class Checkbox : MouseActivatedWidget {
|
class Checkbox : MouseActivatedWidget {
|
||||||
|
|
||||||
override int maxHeight() { return 16; }
|
version(win32_widgets) {
|
||||||
override int minHeight() { return 16; }
|
override int maxHeight() { return 16; }
|
||||||
mixin Margin!"4";
|
override int minHeight() { return 16; }
|
||||||
|
} else version(custom_widgets) {
|
||||||
|
override int maxHeight() { return Window.lineHeight; }
|
||||||
|
override int minHeight() { return Window.lineHeight; }
|
||||||
|
} else static assert(0);
|
||||||
|
|
||||||
|
override int marginLeft() { return 4; }
|
||||||
|
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
this(string label, Widget parent = null) {
|
this(string label, Widget parent = null) {
|
||||||
|
@ -2315,21 +2487,23 @@ class Checkbox : MouseActivatedWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum buttonSize = 16;
|
||||||
|
|
||||||
painter.outlineColor = Color.black;
|
painter.outlineColor = Color.black;
|
||||||
painter.fillColor = Color.white;
|
painter.fillColor = Color.white;
|
||||||
painter.drawRectangle(Point(2, 2), height - 2, height - 2);
|
painter.drawRectangle(Point(2, 2), buttonSize - 2, buttonSize - 2);
|
||||||
|
|
||||||
if(isChecked) {
|
if(isChecked) {
|
||||||
painter.pen = Pen(Color.black, 2);
|
painter.pen = Pen(Color.black, 2);
|
||||||
// I'm using height so the checkbox is square
|
// I'm using height so the checkbox is square
|
||||||
painter.drawLine(Point(6, 6), Point(height - 4, height - 4));
|
enum padding = 5;
|
||||||
painter.drawLine(Point(height-4, 6), Point(6, height - 4));
|
painter.drawLine(Point(padding, padding), Point(buttonSize - (padding-2), buttonSize - (padding-2)));
|
||||||
|
painter.drawLine(Point(buttonSize-(padding-2), padding), Point(padding, buttonSize - (padding-2)));
|
||||||
|
|
||||||
painter.pen = Pen(Color.black, 1);
|
painter.pen = Pen(Color.black, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.drawText(Point(height + 4, 0), label, Point(width, height), TextAlignment.Left | TextAlignment.VerticalCenter);
|
painter.drawText(Point(buttonSize + 4, 0), label, Point(width, height), TextAlignment.Left | TextAlignment.VerticalCenter);
|
||||||
};
|
};
|
||||||
|
|
||||||
defaultEventHandlers["triggered"] = delegate (Widget _this, Event ev) {
|
defaultEventHandlers["triggered"] = delegate (Widget _this, Event ev) {
|
||||||
|
@ -2355,8 +2529,16 @@ class VerticalSpacer : Widget {
|
||||||
|
|
||||||
///
|
///
|
||||||
class Radiobox : MouseActivatedWidget {
|
class Radiobox : MouseActivatedWidget {
|
||||||
override int maxHeight() { return 16; }
|
|
||||||
override int minHeight() { return 16; }
|
version(win32_widgets) {
|
||||||
|
override int maxHeight() { return 16; }
|
||||||
|
override int minHeight() { return 16; }
|
||||||
|
} else version(custom_widgets) {
|
||||||
|
override int maxHeight() { return Window.lineHeight; }
|
||||||
|
override int minHeight() { return Window.lineHeight; }
|
||||||
|
} else static assert(0);
|
||||||
|
|
||||||
|
override int marginLeft() { return 4; }
|
||||||
|
|
||||||
version(win32_widgets)
|
version(win32_widgets)
|
||||||
this(string label, Widget parent = null) {
|
this(string label, Widget parent = null) {
|
||||||
|
@ -2382,18 +2564,19 @@ class Radiobox : MouseActivatedWidget {
|
||||||
|
|
||||||
painter.pen = Pen(Color.black, 1, Pen.Style.Solid);
|
painter.pen = Pen(Color.black, 1, Pen.Style.Solid);
|
||||||
|
|
||||||
|
enum buttonSize = 16;
|
||||||
|
|
||||||
painter.outlineColor = Color.black;
|
painter.outlineColor = Color.black;
|
||||||
painter.fillColor = Color.white;
|
painter.fillColor = Color.white;
|
||||||
painter.drawEllipse(Point(2, 2), Point(height - 2, height - 2));
|
painter.drawEllipse(Point(2, 2), Point(buttonSize - 2, buttonSize - 2));
|
||||||
|
|
||||||
if(isChecked) {
|
if(isChecked) {
|
||||||
painter.outlineColor = Color.black;
|
painter.outlineColor = Color.black;
|
||||||
painter.fillColor = Color.black;
|
painter.fillColor = Color.black;
|
||||||
// I'm using height so the checkbox is square
|
// I'm using height so the checkbox is square
|
||||||
painter.drawEllipse(Point(5, 5), Point(height - 5, height - 5));
|
painter.drawEllipse(Point(5, 5), Point(buttonSize - 5, buttonSize - 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
painter.drawText(Point(height + 4, 0), label, Point(width, height), TextAlignment.Left | TextAlignment.VerticalCenter);
|
painter.drawText(Point(buttonSize + 4, 0), label, Point(width, height), TextAlignment.Left | TextAlignment.VerticalCenter);
|
||||||
};
|
};
|
||||||
|
|
||||||
defaultEventHandlers["triggered"] = delegate (Widget _this, Event ev) {
|
defaultEventHandlers["triggered"] = delegate (Widget _this, Event ev) {
|
||||||
|
@ -2771,6 +2954,13 @@ mixin template EventStuff() {
|
||||||
EventHandler[][string] capturingEventHandlers;
|
EventHandler[][string] capturingEventHandlers;
|
||||||
EventHandler[string] defaultEventHandlers;
|
EventHandler[string] defaultEventHandlers;
|
||||||
|
|
||||||
|
void addDirectEventListener(string event, void delegate() handler, bool useCapture = false) {
|
||||||
|
addEventListener(event, (Widget, Event e) {
|
||||||
|
if(e.srcElement is this)
|
||||||
|
handler();
|
||||||
|
}, useCapture);
|
||||||
|
}
|
||||||
|
|
||||||
void addEventListener(string event, void delegate() handler, bool useCapture = false) {
|
void addEventListener(string event, void delegate() handler, bool useCapture = false) {
|
||||||
addEventListener(event, (Widget, Event) { handler(); }, useCapture);
|
addEventListener(event, (Widget, Event) { handler(); }, useCapture);
|
||||||
}
|
}
|
||||||
|
|
198
simpledisplay.d
198
simpledisplay.d
|
@ -3990,6 +3990,122 @@ void displayImage(Image image, SimpleWindow win = null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FontWeight : int {
|
||||||
|
dontcare = 0,
|
||||||
|
thin = 100,
|
||||||
|
extralight = 200,
|
||||||
|
light = 300,
|
||||||
|
regular = 400,
|
||||||
|
medium = 500,
|
||||||
|
semibold = 600,
|
||||||
|
bold = 700,
|
||||||
|
extrabold = 800,
|
||||||
|
heavy = 900
|
||||||
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Represents a font loaded off the operating system or the X server.
|
||||||
|
|
||||||
|
|
||||||
|
While the api here is unified cross platform, the fonts are not necessarily
|
||||||
|
available, even across machines of the same platform, so be sure to always check
|
||||||
|
for null (using [isNull]) and have a fallback plan.
|
||||||
|
|
||||||
|
When you have a font you like, use [ScreenPainter.setFont] to load it for drawing.
|
||||||
|
|
||||||
|
Worst case, a null font will automatically fall back to the default font loaded
|
||||||
|
for your system.
|
||||||
|
+/
|
||||||
|
class OperatingSystemFont {
|
||||||
|
|
||||||
|
version(X11) {
|
||||||
|
XFontStruct* font;
|
||||||
|
XFontSet fontset;
|
||||||
|
} else version(Windows) {
|
||||||
|
HFONT font;
|
||||||
|
} else static assert(0);
|
||||||
|
|
||||||
|
///
|
||||||
|
this(string name, int size = 0, FontWeight weight = FontWeight.dontcare, bool italic = false) {
|
||||||
|
load(name, size, weight, italic);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
bool load(string name, int size = 0, FontWeight weight = FontWeight.dontcare, bool italic = false) {
|
||||||
|
unload();
|
||||||
|
version(X11) {
|
||||||
|
string weightstr;
|
||||||
|
with(FontWeight)
|
||||||
|
final switch(weight) {
|
||||||
|
case dontcare: weightstr = "*"; break;
|
||||||
|
case thin: weightstr = "extralight"; break;
|
||||||
|
case extralight: weightstr = "extralight"; break;
|
||||||
|
case light: weightstr = "light"; break;
|
||||||
|
case regular: weightstr = "regular"; break;
|
||||||
|
case medium: weightstr = "medium"; break;
|
||||||
|
case semibold: weightstr = "demibold"; break;
|
||||||
|
case bold: weightstr = "bold"; break;
|
||||||
|
case extrabold: weightstr = "demibold"; break;
|
||||||
|
case heavy: weightstr = "black"; break;
|
||||||
|
}
|
||||||
|
string sizestr;
|
||||||
|
if(size == 0)
|
||||||
|
sizestr = "*";
|
||||||
|
else
|
||||||
|
sizestr = "" ~ cast(char)(size / 10 + '0') ~ cast(char)(size % 10 + '0');
|
||||||
|
auto xfontstr = "-*-"~name~"-"~weightstr~"-"~(italic ? "i" : "r")~"-*-*-"~sizestr~"-*-*-*-*-*-*-*";
|
||||||
|
|
||||||
|
//import std.stdio; writeln(xfontstr);
|
||||||
|
|
||||||
|
auto display = XDisplayConnection.get;
|
||||||
|
|
||||||
|
font = XLoadQueryFont(display, xfontstr.ptr);
|
||||||
|
if(font is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char** lol;
|
||||||
|
int lol2;
|
||||||
|
char* lol3;
|
||||||
|
fontset = XCreateFontSet(display, xfontstr.ptr, &lol, &lol2, &lol3);
|
||||||
|
} else version(Windows) {
|
||||||
|
WCharzBuffer buffer = WCharzBuffer(name);
|
||||||
|
font = CreateFont(size, 0, 0, 0, cast(int) weight, italic, 0, 0, 0, 0, 0, 0, 0, buffer.ptr);
|
||||||
|
} else static assert(0);
|
||||||
|
|
||||||
|
return !isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
void unload() {
|
||||||
|
if(isNull())
|
||||||
|
return;
|
||||||
|
|
||||||
|
version(X11) {
|
||||||
|
auto display = XDisplayConnection.get;
|
||||||
|
|
||||||
|
if(font)
|
||||||
|
XFreeFont(display, font);
|
||||||
|
if(fontset)
|
||||||
|
XFreeFontSet(display, fontset);
|
||||||
|
|
||||||
|
font = null;
|
||||||
|
fontset = null;
|
||||||
|
} else version(Windows) {
|
||||||
|
DeleteObject(font);
|
||||||
|
font = null;
|
||||||
|
} else static assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
bool isNull() {
|
||||||
|
return font is null;
|
||||||
|
}
|
||||||
|
|
||||||
|
~this() {
|
||||||
|
unload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
The 2D drawing proxy. You acquire one of these with [SimpleWindow.draw] rather
|
The 2D drawing proxy. You acquire one of these with [SimpleWindow.draw] rather
|
||||||
than constructing it directly. Then, it is reference counted so you can pass it
|
than constructing it directly. Then, it is reference counted so you can pass it
|
||||||
|
@ -4036,6 +4152,11 @@ struct ScreenPainter {
|
||||||
//writeln("refcount ++ ", impl.referenceCount);
|
//writeln("refcount ++ ", impl.referenceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
void setFont(OperatingSystemFont font) {
|
||||||
|
impl.setFont(font);
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
int fontHeight() {
|
int fontHeight() {
|
||||||
return impl.fontHeight();
|
return impl.fontHeight();
|
||||||
|
@ -4200,6 +4321,13 @@ struct ScreenPainter {
|
||||||
impl.drawRectangle(upperLeft.x, upperLeft.y, width, height);
|
impl.drawRectangle(upperLeft.x, upperLeft.y, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void drawRectangle(Point upperLeft, Point lowerRightInclusive) {
|
||||||
|
transform(upperLeft);
|
||||||
|
transform(lowerRightInclusive);
|
||||||
|
impl.drawRectangle(upperLeft.x, upperLeft.y,
|
||||||
|
lowerRightInclusive.x - upperLeft.x + 1, lowerRightInclusive.y - upperLeft.y + 1);
|
||||||
|
}
|
||||||
|
|
||||||
/// Arguments are the points of the bounding rectangle
|
/// Arguments are the points of the bounding rectangle
|
||||||
void drawEllipse(Point upperLeft, Point lowerRight) {
|
void drawEllipse(Point upperLeft, Point lowerRight) {
|
||||||
transform(upperLeft);
|
transform(upperLeft);
|
||||||
|
@ -4215,11 +4343,16 @@ struct ScreenPainter {
|
||||||
|
|
||||||
/// .
|
/// .
|
||||||
void drawPolygon(Point[] vertexes) {
|
void drawPolygon(Point[] vertexes) {
|
||||||
foreach(vertex; vertexes)
|
foreach(ref vertex; vertexes)
|
||||||
transform(vertex);
|
transform(vertex);
|
||||||
impl.drawPolygon(vertexes);
|
impl.drawPolygon(vertexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ditto
|
||||||
|
void drawPolygon(Point[] vertexes...) {
|
||||||
|
drawPolygon(vertexes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// and do a draw/fill in a single call maybe. Windows can do it... but X can't, though it could do two calls.
|
// and do a draw/fill in a single call maybe. Windows can do it... but X can't, though it could do two calls.
|
||||||
|
|
||||||
|
@ -5030,6 +5163,31 @@ version(Windows) {
|
||||||
|
|
||||||
// X doesn't draw a text background, so neither should we
|
// X doesn't draw a text background, so neither should we
|
||||||
SetBkMode(hdc, TRANSPARENT);
|
SetBkMode(hdc, TRANSPARENT);
|
||||||
|
|
||||||
|
|
||||||
|
static bool triedDefaultGuiFont = false;
|
||||||
|
if(!triedDefaultGuiFont) {
|
||||||
|
NONCLIENTMETRICS params;
|
||||||
|
params.cbSize = params.sizeof;
|
||||||
|
if(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, params.sizeof, ¶ms, 0)) {
|
||||||
|
defaultGuiFont = CreateFontIndirect(¶ms.lfMessageFont);
|
||||||
|
}
|
||||||
|
triedDefaultGuiFont = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(defaultGuiFont) {
|
||||||
|
SelectObject(hdc, defaultGuiFont);
|
||||||
|
// DeleteObject(defaultGuiFont);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static HFONT defaultGuiFont;
|
||||||
|
|
||||||
|
void setFont(OperatingSystemFont font) {
|
||||||
|
if(font && font.font)
|
||||||
|
SelectObject(hdc, font.font);
|
||||||
|
else if(defaultGuiFont)
|
||||||
|
SelectObject(hdc, defaultGuiFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
// just because we can on Windows...
|
// just because we can on Windows...
|
||||||
|
@ -6009,9 +6167,13 @@ version(X11) {
|
||||||
// FIXME: should the gc be static too so it isn't recreated every time draw is called?
|
// FIXME: should the gc be static too so it isn't recreated every time draw is called?
|
||||||
GC gc;
|
GC gc;
|
||||||
|
|
||||||
__gshared XFontStruct* font;
|
|
||||||
__gshared bool fontAttempted;
|
__gshared bool fontAttempted;
|
||||||
__gshared XFontSet fontset;
|
|
||||||
|
__gshared XFontStruct* defaultfont;
|
||||||
|
__gshared XFontSet defaultfontset;
|
||||||
|
|
||||||
|
XFontStruct* font;
|
||||||
|
XFontSet fontset;
|
||||||
|
|
||||||
void create(NativeWindowHandle window) {
|
void create(NativeWindowHandle window) {
|
||||||
this.display = XDisplayConnection.get();
|
this.display = XDisplayConnection.get();
|
||||||
|
@ -6039,13 +6201,31 @@ version(X11) {
|
||||||
fontset = XCreateFontSet(display, xfontstr.ptr, &lol, &lol2, &lol3);
|
fontset = XCreateFontSet(display, xfontstr.ptr, &lol, &lol2, &lol3);
|
||||||
|
|
||||||
fontAttempted = true;
|
fontAttempted = true;
|
||||||
|
|
||||||
|
defaultfont = font;
|
||||||
|
defaultfontset = fontset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
font = defaultfont;
|
||||||
|
fontset = defaultfontset;
|
||||||
|
|
||||||
if(font) {
|
if(font) {
|
||||||
XSetFont(display, gc, font.fid);
|
XSetFont(display, gc, font.fid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setFont(OperatingSystemFont font) {
|
||||||
|
if(font && font.font) {
|
||||||
|
this.font = font.font;
|
||||||
|
this.fontset = font.fontset;
|
||||||
|
XSetFont(display, gc, font.font.fid);
|
||||||
|
} else {
|
||||||
|
this.font = defaultfont;
|
||||||
|
this.fontset = defaultfontset;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
auto buffer = this.window.impl.buffer;
|
auto buffer = this.window.impl.buffer;
|
||||||
|
|
||||||
|
@ -6311,8 +6491,12 @@ version(X11) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawPolygon(Point[] vertexes) {
|
void drawPolygon(Point[] vertexes) {
|
||||||
|
XPoint[16] pointsBuffer;
|
||||||
XPoint[] points;
|
XPoint[] points;
|
||||||
points.length = vertexes.length;
|
if(vertexes.length <= pointsBuffer.length)
|
||||||
|
points = pointsBuffer[0 .. vertexes.length];
|
||||||
|
else
|
||||||
|
points.length = vertexes.length;
|
||||||
|
|
||||||
foreach(i, p; vertexes) {
|
foreach(i, p; vertexes) {
|
||||||
points[i].x = cast(short) p.x;
|
points[i].x = cast(short) p.x;
|
||||||
|
@ -10837,6 +11021,7 @@ mixin template ExperimentalTextComponent() {
|
||||||
Point(x, y1),
|
Point(x, y1),
|
||||||
Point(x, y2)
|
Point(x, y2)
|
||||||
);
|
);
|
||||||
|
painter.rasterOp = RasterOp.normal;
|
||||||
caratShowingOnScreen = !caratShowingOnScreen;
|
caratShowingOnScreen = !caratShowingOnScreen;
|
||||||
|
|
||||||
if(caratShowingOnScreen) {
|
if(caratShowingOnScreen) {
|
||||||
|
@ -10856,12 +11041,14 @@ mixin template ExperimentalTextComponent() {
|
||||||
);
|
);
|
||||||
|
|
||||||
caratShowingOnScreen = false;
|
caratShowingOnScreen = false;
|
||||||
|
painter.rasterOp = RasterOp.normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Carat movement api
|
/// Carat movement api
|
||||||
/// These should give the user a logical result based on what they see on screen...
|
/// These should give the user a logical result based on what they see on screen...
|
||||||
/// thus they locate predominately by *pixels* not char index. (These will generally coincide with monospace fonts tho!)
|
/// thus they locate predominately by *pixels* not char index. (These will generally coincide with monospace fonts tho!)
|
||||||
void moveUp(ref Carat carat) {
|
void moveUp(ref Carat carat) {
|
||||||
|
if(carat.inlineElement is null) return;
|
||||||
auto x = carat.inlineElement.xOfIndex(carat.offset);
|
auto x = carat.inlineElement.xOfIndex(carat.offset);
|
||||||
auto y = carat.inlineElement.boundingBox.top + 2;
|
auto y = carat.inlineElement.boundingBox.top + 2;
|
||||||
|
|
||||||
|
@ -10875,6 +11062,7 @@ mixin template ExperimentalTextComponent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void moveDown(ref Carat carat) {
|
void moveDown(ref Carat carat) {
|
||||||
|
if(carat.inlineElement is null) return;
|
||||||
auto x = carat.inlineElement.xOfIndex(carat.offset);
|
auto x = carat.inlineElement.xOfIndex(carat.offset);
|
||||||
auto y = carat.inlineElement.boundingBox.bottom - 2;
|
auto y = carat.inlineElement.boundingBox.bottom - 2;
|
||||||
|
|
||||||
|
@ -10914,6 +11102,7 @@ mixin template ExperimentalTextComponent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void moveHome(ref Carat carat) {
|
void moveHome(ref Carat carat) {
|
||||||
|
if(carat.inlineElement is null) return;
|
||||||
auto x = 0;
|
auto x = 0;
|
||||||
auto y = carat.inlineElement.boundingBox.top + 2;
|
auto y = carat.inlineElement.boundingBox.top + 2;
|
||||||
|
|
||||||
|
@ -10925,6 +11114,7 @@ mixin template ExperimentalTextComponent() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void moveEnd(ref Carat carat) {
|
void moveEnd(ref Carat carat) {
|
||||||
|
if(carat.inlineElement is null) return;
|
||||||
auto x = int.max;
|
auto x = int.max;
|
||||||
auto y = carat.inlineElement.boundingBox.top + 2;
|
auto y = carat.inlineElement.boundingBox.top + 2;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue