mirror of https://github.com/adamdruppe/arsd.git
color widget
This commit is contained in:
parent
c60535deca
commit
06577dee6f
5
color.d
5
color.d
|
@ -1336,7 +1336,7 @@ struct Rectangle {
|
||||||
void floodFill(T)(
|
void floodFill(T)(
|
||||||
T[] what, int width, int height, // the canvas to inspect
|
T[] what, int width, int height, // the canvas to inspect
|
||||||
T target, T replacement, // fill params
|
T target, T replacement, // fill params
|
||||||
int x, int y, bool delegate(int x, int y) additionalCheck) // the node
|
int x, int y, bool delegate(int x, int y) @safe additionalCheck) // the node
|
||||||
{
|
{
|
||||||
T node = what[y * width + x];
|
T node = what[y * width + x];
|
||||||
|
|
||||||
|
@ -1344,6 +1344,9 @@ void floodFill(T)(
|
||||||
|
|
||||||
if(node != target) return;
|
if(node != target) return;
|
||||||
|
|
||||||
|
if(additionalCheck is null)
|
||||||
|
additionalCheck = (int, int) => true;
|
||||||
|
|
||||||
if(!additionalCheck(x, y))
|
if(!additionalCheck(x, y))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
276
minigui.d
276
minigui.d
|
@ -67,14 +67,17 @@ version(Windows) {
|
||||||
// use native widgets when available unless specifically asked otherwise
|
// use native widgets when available unless specifically asked otherwise
|
||||||
version(custom_widgets) {
|
version(custom_widgets) {
|
||||||
enum bool UsingCustomWidgets = true;
|
enum bool UsingCustomWidgets = true;
|
||||||
|
enum bool UsingWin32Widgets = false;
|
||||||
} else {
|
} else {
|
||||||
version = win32_widgets;
|
version = win32_widgets;
|
||||||
enum bool UsingCustomWidgets = false;
|
enum bool UsingCustomWidgets = false;
|
||||||
|
enum bool UsingWin32Widgets = true;
|
||||||
}
|
}
|
||||||
// and native theming when needed
|
// and native theming when needed
|
||||||
//version = win32_theming;
|
//version = win32_theming;
|
||||||
} else {
|
} else {
|
||||||
enum bool UsingCustomWidgets = true;
|
enum bool UsingCustomWidgets = true;
|
||||||
|
enum bool UsingWin32Widgets = false;
|
||||||
version=custom_widgets;
|
version=custom_widgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -729,6 +732,8 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
||||||
|
|
||||||
int stretchinessSum;
|
int stretchinessSum;
|
||||||
int lastMargin = 0;
|
int lastMargin = 0;
|
||||||
|
|
||||||
|
// set initial size
|
||||||
foreach(child; parent.children) {
|
foreach(child; parent.children) {
|
||||||
if(cast(StaticPosition) child)
|
if(cast(StaticPosition) child)
|
||||||
continue;
|
continue;
|
||||||
|
@ -748,13 +753,13 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
||||||
child.width = child.maxWidth();
|
child.width = child.maxWidth();
|
||||||
child.height = child.minHeight();
|
child.height = child.minHeight();
|
||||||
} else {
|
} else {
|
||||||
if(child.height < 0)
|
|
||||||
child.height = 0;
|
|
||||||
child.height = parent.height -
|
child.height = parent.height -
|
||||||
mixin("child.margin"~firstThingy~"()") -
|
mixin("child.margin"~firstThingy~"()") -
|
||||||
mixin("child.margin"~secondThingy~"()") -
|
mixin("child.margin"~secondThingy~"()") -
|
||||||
mixin("parent.padding"~firstThingy~"()") -
|
mixin("parent.padding"~firstThingy~"()") -
|
||||||
mixin("parent.padding"~secondThingy~"()");
|
mixin("parent.padding"~secondThingy~"()");
|
||||||
|
if(child.height < 0)
|
||||||
|
child.height = 0;
|
||||||
if(child.height > child.maxHeight())
|
if(child.height > child.maxHeight())
|
||||||
child.height = child.maxHeight();
|
child.height = child.maxHeight();
|
||||||
child.width = child.minWidth();
|
child.width = child.minWidth();
|
||||||
|
@ -769,7 +774,7 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
||||||
stretchinessSum += mixin("child." ~ relevantMeasure ~ "Stretchiness()");
|
stretchinessSum += mixin("child." ~ relevantMeasure ~ "Stretchiness()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stretch to fill space
|
||||||
while(spaceRemaining > 0 && stretchinessSum) {
|
while(spaceRemaining > 0 && stretchinessSum) {
|
||||||
//import std.stdio; writeln("str ", stretchinessSum);
|
//import std.stdio; writeln("str ", stretchinessSum);
|
||||||
auto spacePerChild = spaceRemaining / stretchinessSum;
|
auto spacePerChild = spaceRemaining / stretchinessSum;
|
||||||
|
@ -809,6 +814,7 @@ void recomputeChildLayout(string relevantMeasure)(Widget parent) {
|
||||||
break; // apparently nothing more we can do
|
break; // apparently nothing more we can do
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// position
|
||||||
lastMargin = 0;
|
lastMargin = 0;
|
||||||
int currentPos = mixin("parent.padding"~firstThingy~"()");
|
int currentPos = mixin("parent.padding"~firstThingy~"()");
|
||||||
foreach(child; parent.children) {
|
foreach(child; parent.children) {
|
||||||
|
@ -1250,6 +1256,9 @@ enum ScrollBarShowPolicy {
|
||||||
|
|
||||||
/++
|
/++
|
||||||
+/
|
+/
|
||||||
|
version(win32_widgets)
|
||||||
|
class ScrollableWidget : Widget { this(Widget parent = null) { super(parent); } } // TEMPORARY
|
||||||
|
else
|
||||||
class ScrollableWidget : Widget {
|
class ScrollableWidget : Widget {
|
||||||
this(Widget parent = null) {
|
this(Widget parent = null) {
|
||||||
horizontalScrollbarHolder = new FixedPosition(this);
|
horizontalScrollbarHolder = new FixedPosition(this);
|
||||||
|
@ -1271,15 +1280,48 @@ class ScrollableWidget : Widget {
|
||||||
|
|
||||||
override void recomputeChildLayout() {
|
override void recomputeChildLayout() {
|
||||||
bool both = showingVerticalScroll && showingHorizontalScroll;
|
bool both = showingVerticalScroll && showingHorizontalScroll;
|
||||||
horizontalScrollbarHolder.width = this.width - (both ? 16 : 0);
|
if(horizontalScrollbarHolder && verticalScrollbarHolder) {
|
||||||
horizontalScrollbarHolder.height = 16;
|
horizontalScrollbarHolder.width = this.width - (both ? 16 : 0);
|
||||||
horizontalScrollbarHolder.x = 0;
|
horizontalScrollbarHolder.height = 16;
|
||||||
horizontalScrollbarHolder.y = this.height - 16;
|
horizontalScrollbarHolder.x = 0;
|
||||||
|
horizontalScrollbarHolder.y = this.height - 16;
|
||||||
|
|
||||||
|
verticalScrollbarHolder.width = 16;
|
||||||
|
verticalScrollbarHolder.height = this.height - (both ? 16 : 0);
|
||||||
|
verticalScrollbarHolder.x = this.width - 16;
|
||||||
|
verticalScrollbarHolder.y = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
int viewableScrollArea = viewportHeight;
|
||||||
|
int totalScrollArea = contentHeight;
|
||||||
|
int totalScrollBarArea = verticalScrollBar.thumb.height;
|
||||||
|
int thumbSize;
|
||||||
|
if(totalScrollArea)
|
||||||
|
thumbSize = viewableScrollArea * totalScrollBarArea / totalScrollArea;
|
||||||
|
else
|
||||||
|
thumbSize = 0;
|
||||||
|
if(thumbSize < 6)
|
||||||
|
thumbSize = 6;
|
||||||
|
|
||||||
|
verticalScrollBar.thumb.thumbHeight = thumbSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int viewableScrollArea = viewportWidth;
|
||||||
|
int totalScrollArea = contentWidth;
|
||||||
|
int totalScrollBarArea = horizontalScrollBar.thumb.width;
|
||||||
|
int thumbSize;
|
||||||
|
if(totalScrollArea)
|
||||||
|
thumbSize = viewableScrollArea * totalScrollBarArea / totalScrollArea;
|
||||||
|
else
|
||||||
|
thumbSize = 0;
|
||||||
|
if(thumbSize < 6)
|
||||||
|
thumbSize = 6;
|
||||||
|
|
||||||
|
horizontalScrollBar.thumb.thumbWidth = thumbSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
verticalScrollbarHolder.width = 16;
|
|
||||||
verticalScrollbarHolder.height = this.height - (both ? 16 : 0);
|
|
||||||
verticalScrollbarHolder.x = this.width - 16;
|
|
||||||
verticalScrollbarHolder.y = 0;
|
|
||||||
|
|
||||||
super.recomputeChildLayout();
|
super.recomputeChildLayout();
|
||||||
}
|
}
|
||||||
|
@ -1322,32 +1364,10 @@ class ScrollableWidget : Widget {
|
||||||
contentWidth = width;
|
contentWidth = width;
|
||||||
contentHeight = height;
|
contentHeight = height;
|
||||||
|
|
||||||
if(showingVerticalScroll && showingHorizontalScroll) {
|
if(showingVerticalScroll || showingHorizontalScroll) {
|
||||||
recomputeChildLayout();
|
recomputeChildLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
int viewableScrollArea = viewportHeight;
|
|
||||||
int totalScrollArea = contentHeight;
|
|
||||||
int totalScrollBarArea = verticalScrollBar.thumb.height;
|
|
||||||
int thumbSize = viewableScrollArea * totalScrollBarArea / totalScrollArea;
|
|
||||||
|
|
||||||
verticalScrollBar.thumb.thumbHeight = thumbSize;
|
|
||||||
if(showingVerticalScroll())
|
|
||||||
verticalScrollBar.redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
int viewableScrollArea = viewportWidth;
|
|
||||||
int totalScrollArea = contentWidth;
|
|
||||||
int totalScrollBarArea = horizontalScrollBar.thumb.width;
|
|
||||||
int thumbSize = viewableScrollArea * totalScrollBarArea / totalScrollArea;
|
|
||||||
|
|
||||||
horizontalScrollBar.thumb.thumbWidth = thumbSize;
|
|
||||||
if(showingHorizontalScroll())
|
|
||||||
horizontalScrollBar.redraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if(showingHorizontalScroll())
|
if(showingHorizontalScroll())
|
||||||
horizontalScrollbarHolder.hidden = false;
|
horizontalScrollbarHolder.hidden = false;
|
||||||
|
@ -1358,6 +1378,10 @@ class ScrollableWidget : Widget {
|
||||||
else
|
else
|
||||||
verticalScrollbarHolder.hidden = true;
|
verticalScrollbarHolder.hidden = true;
|
||||||
|
|
||||||
|
if(showingVerticalScroll())
|
||||||
|
verticalScrollBar.redraw();
|
||||||
|
if(showingHorizontalScroll())
|
||||||
|
horizontalScrollBar.redraw();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1486,6 +1510,7 @@ abstract class ScrollbarBase : Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
version(custom_widgets)
|
||||||
class HorizontalScrollbar : ScrollbarBase {
|
class HorizontalScrollbar : ScrollbarBase {
|
||||||
|
|
||||||
MouseTrackingWidget thumb;
|
MouseTrackingWidget thumb;
|
||||||
|
@ -1538,7 +1563,8 @@ class HorizontalScrollbar : ScrollbarBase {
|
||||||
override int minWidth() { return 48; }
|
override int minWidth() { return 48; }
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///show
|
||||||
|
version(custom_widgets)
|
||||||
class VerticalScrollbar : ScrollbarBase {
|
class VerticalScrollbar : ScrollbarBase {
|
||||||
|
|
||||||
MouseTrackingWidget thumb;
|
MouseTrackingWidget thumb;
|
||||||
|
@ -1596,6 +1622,7 @@ class VerticalScrollbar : ScrollbarBase {
|
||||||
|
|
||||||
Concrete subclasses may include a scrollbar thumb and a volume control.
|
Concrete subclasses may include a scrollbar thumb and a volume control.
|
||||||
+/
|
+/
|
||||||
|
version(custom_widgets)
|
||||||
class MouseTrackingWidget : Widget {
|
class MouseTrackingWidget : Widget {
|
||||||
int mouseTrackerPosition;
|
int mouseTrackerPosition;
|
||||||
|
|
||||||
|
@ -1625,11 +1652,31 @@ class MouseTrackingWidget : Widget {
|
||||||
addEventListener(EventType.mousedown, (Event event) {
|
addEventListener(EventType.mousedown, (Event event) {
|
||||||
if(event.clientX >= positionX && event.clientX < positionX + thumbWidth && event.clientY >= positionY && event.clientY < positionY + thumbHeight) {
|
if(event.clientX >= positionX && event.clientX < positionX + thumbWidth && event.clientY >= positionY && event.clientY < positionY + thumbHeight) {
|
||||||
dragging = true;
|
dragging = true;
|
||||||
startMouseX = event.clientX;
|
startMouseX = event.clientX - positionX;
|
||||||
startMouseY = event.clientY;
|
startMouseY = event.clientY - positionY;
|
||||||
parentWindow.captureMouse(this);
|
parentWindow.captureMouse(this);
|
||||||
} else {
|
} else {
|
||||||
// FIXME
|
if(orientation == Orientation.horizontal || orientation == Orientation.twoDimensional)
|
||||||
|
positionX = event.clientX - thumbWidth / 2;
|
||||||
|
if(orientation == Orientation.vertical || orientation == Orientation.twoDimensional)
|
||||||
|
positionY = event.clientY - thumbHeight / 2;
|
||||||
|
|
||||||
|
if(positionX + thumbWidth > this.width)
|
||||||
|
positionX = this.width - thumbWidth;
|
||||||
|
if(positionY + thumbHeight > this.height)
|
||||||
|
positionY = this.height - thumbHeight;
|
||||||
|
|
||||||
|
if(positionX < 0)
|
||||||
|
positionX = 0;
|
||||||
|
if(positionY < 0)
|
||||||
|
positionY = 0;
|
||||||
|
|
||||||
|
|
||||||
|
auto evt = new Event(EventType.change, this);
|
||||||
|
evt.sendDirectly();
|
||||||
|
|
||||||
|
redraw();
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1659,7 +1706,7 @@ class MouseTrackingWidget : Widget {
|
||||||
}
|
}
|
||||||
|
|
||||||
if(orientation == Orientation.horizontal || orientation == Orientation.twoDimensional)
|
if(orientation == Orientation.horizontal || orientation == Orientation.twoDimensional)
|
||||||
positionX = event.clientX - startMouseX;
|
positionX = event.clientX - startMouseX; // FIXME: click could be in the middle of it
|
||||||
if(orientation == Orientation.vertical || orientation == Orientation.twoDimensional)
|
if(orientation == Orientation.vertical || orientation == Orientation.twoDimensional)
|
||||||
positionY = event.clientY - startMouseY;
|
positionY = event.clientY - startMouseY;
|
||||||
|
|
||||||
|
@ -1702,6 +1749,98 @@ abstract class Layout : Widget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
Makes all children minimum width and height, placing them down
|
||||||
|
left to right, top to bottom.
|
||||||
|
|
||||||
|
Useful if you want to make a list of buttons that automatically
|
||||||
|
wrap to a new line when necessary.
|
||||||
|
+/
|
||||||
|
class InlineBlockLayout : Layout {
|
||||||
|
this(Widget parent = null) { super(parent); }
|
||||||
|
|
||||||
|
override void recomputeChildLayout() {
|
||||||
|
registerMovement();
|
||||||
|
|
||||||
|
int x = this.paddingLeft, y = this.paddingTop;
|
||||||
|
|
||||||
|
int lineHeight;
|
||||||
|
int previousMargin = 0;
|
||||||
|
int previousMarginBottom = 0;
|
||||||
|
|
||||||
|
foreach(child; children) {
|
||||||
|
if(child.hidden)
|
||||||
|
continue;
|
||||||
|
if(cast(FixedPosition) child) {
|
||||||
|
child.recomputeChildLayout();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
child.width = child.minWidth();
|
||||||
|
if(child.width == 0)
|
||||||
|
child.width = 32;
|
||||||
|
child.height = child.minHeight();
|
||||||
|
if(child.height == 0)
|
||||||
|
child.height = 32;
|
||||||
|
|
||||||
|
if(x + child.width + paddingRight > this.width) {
|
||||||
|
x = this.paddingLeft;
|
||||||
|
y += lineHeight;
|
||||||
|
lineHeight = 0;
|
||||||
|
previousMargin = 0;
|
||||||
|
previousMarginBottom = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto margin = child.marginLeft;
|
||||||
|
if(previousMargin > margin)
|
||||||
|
margin = previousMargin;
|
||||||
|
|
||||||
|
x += margin;
|
||||||
|
|
||||||
|
child.x = x;
|
||||||
|
child.y = y;
|
||||||
|
|
||||||
|
int marginTopApplied;
|
||||||
|
if(child.marginTop > previousMarginBottom) {
|
||||||
|
child.y += child.marginTop;
|
||||||
|
marginTopApplied = child.marginTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
x += child.width;
|
||||||
|
previousMargin = child.marginRight;
|
||||||
|
|
||||||
|
if(child.marginBottom > previousMarginBottom)
|
||||||
|
previousMarginBottom = child.marginBottom;
|
||||||
|
|
||||||
|
auto h = child.height + previousMarginBottom + marginTopApplied;
|
||||||
|
if(h > lineHeight)
|
||||||
|
lineHeight = h;
|
||||||
|
|
||||||
|
child.recomputeChildLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override int minWidth() {
|
||||||
|
int min;
|
||||||
|
foreach(child; children) {
|
||||||
|
auto cm = child.minWidth;
|
||||||
|
if(cm > min)
|
||||||
|
min = cm;
|
||||||
|
}
|
||||||
|
return min + paddingLeft + paddingRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
override int minHeight() {
|
||||||
|
int min;
|
||||||
|
foreach(child; children) {
|
||||||
|
auto cm = child.minHeight;
|
||||||
|
if(cm > min)
|
||||||
|
min = cm;
|
||||||
|
}
|
||||||
|
return min + paddingTop + paddingBottom;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Stacks the widgets vertically, taking all the available width for each child.
|
/// Stacks the widgets vertically, taking all the available width for each child.
|
||||||
class VerticalLayout : Layout {
|
class VerticalLayout : Layout {
|
||||||
// intentionally blank - widget's default is vertical layout right now
|
// intentionally blank - widget's default is vertical layout right now
|
||||||
|
@ -2033,6 +2172,7 @@ class Window : Widget {
|
||||||
auto event = new Event(ev.pressed ? "keydown" : "keyup", focusedWidget);
|
auto event = new Event(ev.pressed ? "keydown" : "keyup", focusedWidget);
|
||||||
event.character = ev.character;
|
event.character = ev.character;
|
||||||
event.key = ev.key;
|
event.key = ev.key;
|
||||||
|
event.state = ev.modifierState;
|
||||||
event.shiftKey = (ev.modifierState & ModifierState.shift) ? true : false;
|
event.shiftKey = (ev.modifierState & ModifierState.shift) ? true : false;
|
||||||
event.dispatch();
|
event.dispatch();
|
||||||
}
|
}
|
||||||
|
@ -2146,6 +2286,15 @@ class Window : Widget {
|
||||||
win.eventLoop(0);
|
win.eventLoop(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override void show() {
|
||||||
|
win.show();
|
||||||
|
super.show();
|
||||||
|
}
|
||||||
|
override void hide() {
|
||||||
|
win.hide();
|
||||||
|
super.hide();
|
||||||
|
}
|
||||||
|
|
||||||
static Widget getFirstFocusable(Widget start) {
|
static Widget getFirstFocusable(Widget start) {
|
||||||
if(start.tabStop && !start.hidden)
|
if(start.tabStop && !start.hidden)
|
||||||
return start;
|
return start;
|
||||||
|
@ -2160,6 +2309,48 @@ class Window : Widget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/++
|
||||||
|
A dialog is a transient window that intends to get information from
|
||||||
|
the user before being dismissed.
|
||||||
|
+/
|
||||||
|
abstract class Dialog : Window {
|
||||||
|
///
|
||||||
|
this(int width, int height, string title = null) {
|
||||||
|
super(width, height, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
abstract void OK();
|
||||||
|
|
||||||
|
///
|
||||||
|
void Cancel() {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
class LabeledLineEdit : Widget {
|
||||||
|
this(string label, Widget parent = null) {
|
||||||
|
super(parent);
|
||||||
|
tabStop = false;
|
||||||
|
auto hl = new HorizontalLayout(this);
|
||||||
|
this.label = new TextLabel(label, hl);
|
||||||
|
this.lineEdit = new LineEdit(hl);
|
||||||
|
}
|
||||||
|
TextLabel label; ///
|
||||||
|
LineEdit lineEdit; ///
|
||||||
|
|
||||||
|
override int minHeight() { return Window.lineHeight + 4; }
|
||||||
|
override int maxHeight() { return Window.lineHeight + 4; }
|
||||||
|
|
||||||
|
string content() {
|
||||||
|
return lineEdit.content;
|
||||||
|
}
|
||||||
|
void content(string c) {
|
||||||
|
return lineEdit.content(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
class MainWindow : Window {
|
class MainWindow : Window {
|
||||||
this(string title = null) {
|
this(string title = null) {
|
||||||
|
@ -3298,6 +3489,7 @@ int[2] getChildPositionRelativeToParentHwnd(Widget c) nothrow {
|
||||||
class TextLabel : Widget {
|
class TextLabel : Widget {
|
||||||
override int maxHeight() { return Window.lineHeight; }
|
override int maxHeight() { return Window.lineHeight; }
|
||||||
override int minHeight() { return Window.lineHeight; }
|
override int minHeight() { return Window.lineHeight; }
|
||||||
|
override int minWidth() { return 32; }
|
||||||
|
|
||||||
string label;
|
string label;
|
||||||
this(string label, Widget parent = null) {
|
this(string label, Widget parent = null) {
|
||||||
|
@ -3322,6 +3514,7 @@ abstract class EditableTextWidget : ScrollableWidget {
|
||||||
super(parent);
|
super(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override int minWidth() { return 16; }
|
||||||
override int minHeight() { return Window.lineHeight + 0; } // the +0 is to leave room for the padding
|
override int minHeight() { return Window.lineHeight + 0; } // the +0 is to leave room for the padding
|
||||||
override int widthStretchiness() { return 3; }
|
override int widthStretchiness() { return 3; }
|
||||||
|
|
||||||
|
@ -3422,6 +3615,9 @@ abstract class EditableTextWidget : ScrollableWidget {
|
||||||
caratTimer.destroy();
|
caratTimer.destroy();
|
||||||
caratTimer = null;
|
caratTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto evt = new Event(EventType.change, this);
|
||||||
|
evt.dispatch();
|
||||||
};
|
};
|
||||||
|
|
||||||
defaultEventHandlers["char"] = delegate (Widget _this, Event ev) {
|
defaultEventHandlers["char"] = delegate (Widget _this, Event ev) {
|
||||||
|
@ -3680,6 +3876,7 @@ class Event {
|
||||||
private bool isBubbling;
|
private bool isBubbling;
|
||||||
|
|
||||||
private void adjustScrolling() {
|
private void adjustScrolling() {
|
||||||
|
version(custom_widgets) { // TEMP
|
||||||
viewportX = clientX;
|
viewportX = clientX;
|
||||||
viewportY = clientY;
|
viewportY = clientY;
|
||||||
if(auto se = cast(ScrollableWidget) srcElement) {
|
if(auto se = cast(ScrollableWidget) srcElement) {
|
||||||
|
@ -3687,6 +3884,7 @@ class Event {
|
||||||
clientY += se.scrollOrigin.y;
|
clientY += se.scrollOrigin.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// this sends it only to the target. If you want propagation, use dispatch() instead.
|
/// this sends it only to the target. If you want propagation, use dispatch() instead.
|
||||||
void sendDirectly() {
|
void sendDirectly() {
|
||||||
|
|
|
@ -0,0 +1,207 @@
|
||||||
|
/++
|
||||||
|
Displays a color-picker dialog box.
|
||||||
|
+/
|
||||||
|
module arsd.minigui_addons.color_dialog;
|
||||||
|
|
||||||
|
import arsd.minigui;
|
||||||
|
|
||||||
|
static if(UsingWin32Widgets)
|
||||||
|
pragma(lib, "comdlg32");
|
||||||
|
|
||||||
|
/++
|
||||||
|
|
||||||
|
+/
|
||||||
|
void showColorDialog(Window owner, Color current, void delegate(Color choice) onOK, void delegate() onCancel = null) {
|
||||||
|
static if(UsingWin32Widgets) {
|
||||||
|
import core.sys.windows.windows;
|
||||||
|
static COLORREF[16] customColors;
|
||||||
|
CHOOSECOLOR cc;
|
||||||
|
cc.lStructSize = cc.sizeof;
|
||||||
|
cc.hwndOwner = owner ? owner.win.impl.hwnd : null;
|
||||||
|
cc.lpCustColors = cast(LPDWORD) customColors.ptr;
|
||||||
|
cc.rgbResult = RGB(current.r, current.g, current.b);
|
||||||
|
cc.Flags = CC_FULLOPEN | CC_RGBINIT;
|
||||||
|
if(ChooseColor(&cc)) {
|
||||||
|
onOK(Color(GetRValue(cc.rgbResult), GetGValue(cc.rgbResult), GetBValue(cc.rgbResult)));
|
||||||
|
} else {
|
||||||
|
if(onCancel)
|
||||||
|
onCancel();
|
||||||
|
}
|
||||||
|
} else static if(UsingCustomWidgets) {
|
||||||
|
auto cpd = new ColorPickerDialog(current, onOK, owner);
|
||||||
|
cpd.show();
|
||||||
|
} else static assert(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Hue / Saturation picker
|
||||||
|
Lightness Picker
|
||||||
|
|
||||||
|
Text selections
|
||||||
|
|
||||||
|
Graphical representation
|
||||||
|
|
||||||
|
Cancel OK
|
||||||
|
*/
|
||||||
|
|
||||||
|
static if(UsingCustomWidgets)
|
||||||
|
class ColorPickerDialog : Dialog {
|
||||||
|
static arsd.simpledisplay.Sprite hslImage;
|
||||||
|
|
||||||
|
static bool canUseImage;
|
||||||
|
|
||||||
|
void delegate(Color) onOK;
|
||||||
|
|
||||||
|
this(Color current, void delegate(Color) onOK, Window owner) {
|
||||||
|
super(360, 350, "Color picker");
|
||||||
|
|
||||||
|
this.onOK = onOK;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
statusBar.parts ~= new StatusBar.Part(140);
|
||||||
|
statusBar.parts ~= new StatusBar.Part(140);
|
||||||
|
statusBar.parts ~= new StatusBar.Part(140);
|
||||||
|
statusBar.parts ~= new StatusBar.Part(140);
|
||||||
|
this.addEventListener("mouseover", (Event ev) {
|
||||||
|
import std.conv;
|
||||||
|
this.statusBar.parts[2].content = to!string(ev.target.minHeight) ~ " - " ~ to!string(ev.target.maxHeight);
|
||||||
|
this.statusBar.parts[3].content = ev.target.toString();
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
static if(UsingSimpledisplayX11)
|
||||||
|
// it is brutally slow over the network if we don't
|
||||||
|
// have xshm, so we've gotta do something else.
|
||||||
|
canUseImage = Image.impl.xshmAvailable;
|
||||||
|
else
|
||||||
|
canUseImage = true;
|
||||||
|
|
||||||
|
if(hslImage is null && canUseImage) {
|
||||||
|
auto img = new TrueColorImage(180, 128);
|
||||||
|
double h = 0.0, s = 1.0, l = 0.5;
|
||||||
|
foreach(y; 0 .. img.height) {
|
||||||
|
foreach(x; 0 .. img.width) {
|
||||||
|
img.imageData.colors[y * img.width + x] = Color.fromHsl(h,s,l);
|
||||||
|
h += 360.0 / img.width;
|
||||||
|
}
|
||||||
|
h = 0.0;
|
||||||
|
s -= 1.0 / img.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
hslImage = new arsd.simpledisplay.Sprite(this.win, Image.fromMemoryImage(img));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto t = this;
|
||||||
|
|
||||||
|
auto wid = new class Widget {
|
||||||
|
this() { super(t); }
|
||||||
|
override int minHeight() { return hslImage ? hslImage.height : 4; }
|
||||||
|
override int maxHeight() { return hslImage ? hslImage.height : 4; }
|
||||||
|
};
|
||||||
|
wid.paint = (ScreenPainter painter) {
|
||||||
|
if(hslImage)
|
||||||
|
hslImage.drawAt(painter, Point(0, 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto vlRgb = new class VerticalLayout {
|
||||||
|
this() {
|
||||||
|
super(t);
|
||||||
|
}
|
||||||
|
override int maxWidth() { return 150; };
|
||||||
|
};
|
||||||
|
|
||||||
|
r = new LabeledLineEdit("Red:", vlRgb);
|
||||||
|
g = new LabeledLineEdit("Green:", vlRgb);
|
||||||
|
b = new LabeledLineEdit("Blue:", vlRgb);
|
||||||
|
a = new LabeledLineEdit("Alpha:", vlRgb);
|
||||||
|
|
||||||
|
import std.conv;
|
||||||
|
|
||||||
|
r.content = to!string(current.r);
|
||||||
|
g.content = to!string(current.g);
|
||||||
|
b.content = to!string(current.b);
|
||||||
|
a.content = to!string(current.a);
|
||||||
|
|
||||||
|
|
||||||
|
if(hslImage !is null)
|
||||||
|
wid.addEventListener("mousedown", (Event event) {
|
||||||
|
auto h = cast(double) event.clientX / hslImage.width * 360.0;
|
||||||
|
auto s = 1.0 - (cast(double) event.clientY / hslImage.height * 1.0);
|
||||||
|
auto l = 0.5;
|
||||||
|
|
||||||
|
auto color = Color.fromHsl(h, s, l);
|
||||||
|
|
||||||
|
r.content = to!string(color.r);
|
||||||
|
g.content = to!string(color.g);
|
||||||
|
b.content = to!string(color.b);
|
||||||
|
a.content = to!string(color.a);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
Color currentColor() {
|
||||||
|
try {
|
||||||
|
return Color(to!int(r.content), to!int(g.content), to!int(b.content), to!int(a.content));
|
||||||
|
} catch(Exception e) {
|
||||||
|
return Color.transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addEventListener("keydown", (Event event) {
|
||||||
|
if(event.key == Key.Enter)
|
||||||
|
OK();
|
||||||
|
if(event.character == Key.Escape)
|
||||||
|
Cancel();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addEventListener("change", {
|
||||||
|
redraw();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto currentColorWidget = new Widget(this);
|
||||||
|
currentColorWidget.paint = (ScreenPainter painter) {
|
||||||
|
auto c = currentColor();
|
||||||
|
|
||||||
|
auto c1 = alphaBlend(c, Color(64, 64, 64));
|
||||||
|
auto c2 = alphaBlend(c, Color(192, 192, 192));
|
||||||
|
|
||||||
|
painter.outlineColor = c1;
|
||||||
|
painter.fillColor = c1;
|
||||||
|
painter.drawRectangle(Point(0, 0), currentColorWidget.width / 2, currentColorWidget.height / 2);
|
||||||
|
painter.drawRectangle(Point(currentColorWidget.width / 2, currentColorWidget.height / 2), currentColorWidget.width / 2, currentColorWidget.height / 2);
|
||||||
|
|
||||||
|
painter.outlineColor = c2;
|
||||||
|
painter.fillColor = c2;
|
||||||
|
painter.drawRectangle(Point(currentColorWidget.width / 2, 0), currentColorWidget.width / 2, currentColorWidget.height / 2);
|
||||||
|
painter.drawRectangle(Point(0, currentColorWidget.height / 2), currentColorWidget.width / 2, currentColorWidget.height / 2);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto hl = new HorizontalLayout(this);
|
||||||
|
auto cancelButton = new Button("Cancel", hl);
|
||||||
|
auto okButton = new Button("OK", hl);
|
||||||
|
|
||||||
|
recomputeChildLayout(); // FIXME hack
|
||||||
|
|
||||||
|
cancelButton.addEventListener(EventType.triggered, &Cancel);
|
||||||
|
okButton.addEventListener(EventType.triggered, &OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
LabeledLineEdit r;
|
||||||
|
LabeledLineEdit g;
|
||||||
|
LabeledLineEdit b;
|
||||||
|
LabeledLineEdit a;
|
||||||
|
|
||||||
|
override void OK() {
|
||||||
|
import std.conv;
|
||||||
|
try {
|
||||||
|
onOK(Color(to!int(r.content), to!int(g.content), to!int(b.content), to!int(a.content)));
|
||||||
|
this.close();
|
||||||
|
} catch(Exception e) {
|
||||||
|
auto mb = new MessageBox("Bad value");
|
||||||
|
mb.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/++
|
||||||
|
This package consists of additional widgets for [arsd.minigui].
|
||||||
|
|
||||||
|
Each module stands alone on top of minigui.d; none in this package
|
||||||
|
depend on each other, so you can pick and choose the modules that
|
||||||
|
look useful to you and ignore the others.
|
||||||
|
|
||||||
|
These modules may or may not expose native widgets, refer to the
|
||||||
|
documentation in each individual to see what it does.
|
||||||
|
|
||||||
|
|
||||||
|
When writing a minigui addon module, keep the following in mind:
|
||||||
|
|
||||||
|
$(LIST
|
||||||
|
* Use `static if(UsingWin32Widgets)` and `static if(UsingCustomWidgets)`
|
||||||
|
if you want to provide both native Windows and custom drawn alternatives.
|
||||||
|
Do NOT use `version` because versions are not imported across modules.
|
||||||
|
|
||||||
|
* Similarly, if you need to write platform-specific code, you can use
|
||||||
|
`static if(UsingSimpledisplayX11)` to check for X. However, here,
|
||||||
|
`version(Windows)` also works pretty well.
|
||||||
|
|
||||||
|
)
|
||||||
|
+/
|
||||||
|
module arsd.minigui_addons;
|
Loading…
Reference in New Issue