scrollbars omg

This commit is contained in:
Adam D. Ruppe 2020-03-21 20:33:59 -04:00
parent abb98b54df
commit a45a344525
1 changed files with 331 additions and 15 deletions

342
minigui.d
View File

@ -1862,6 +1862,7 @@ enum ScrollBarShowPolicy {
/++ /++
FIXME ScrollBarShowPolicy FIXME ScrollBarShowPolicy
FIXME: use the ScrollMessageWidget in here now that it exists
+/ +/
class ScrollableWidget : Widget { class ScrollableWidget : Widget {
// FIXME: make line size configurable // FIXME: make line size configurable
@ -2291,13 +2292,53 @@ private class ScrollableContainerWidget : Widget {
horizontalScrollBar.showing_ = false; horizontalScrollBar.showing_ = false;
verticalScrollBar.showing_ = false; verticalScrollBar.showing_ = false;
horizontalScrollBar.addEventListener(EventType.change, () { horizontalScrollBar.addEventListener("scrolltonextline", {
horizontalScrollBar.setPosition(horizontalScrollBar.position + 1);
sw.horizontalScrollTo(horizontalScrollBar.position); sw.horizontalScrollTo(horizontalScrollBar.position);
}); });
verticalScrollBar.addEventListener(EventType.change, () { horizontalScrollBar.addEventListener("scrolltopreviousline", {
horizontalScrollBar.setPosition(horizontalScrollBar.position - 1);
sw.horizontalScrollTo(horizontalScrollBar.position);
});
verticalScrollBar.addEventListener("scrolltonextline", {
verticalScrollBar.setPosition(verticalScrollBar.position + 1);
sw.verticalScrollTo(verticalScrollBar.position); sw.verticalScrollTo(verticalScrollBar.position);
}); });
verticalScrollBar.addEventListener("scrolltopreviousline", {
verticalScrollBar.setPosition(verticalScrollBar.position - 1);
sw.verticalScrollTo(verticalScrollBar.position);
});
horizontalScrollBar.addEventListener("scrolltonextpage", {
horizontalScrollBar.setPosition(horizontalScrollBar.position + horizontalScrollBar.step_);
sw.horizontalScrollTo(horizontalScrollBar.position);
});
horizontalScrollBar.addEventListener("scrolltopreviouspage", {
horizontalScrollBar.setPosition(horizontalScrollBar.position - horizontalScrollBar.step_);
sw.horizontalScrollTo(horizontalScrollBar.position);
});
verticalScrollBar.addEventListener("scrolltonextpage", {
verticalScrollBar.setPosition(verticalScrollBar.position + verticalScrollBar.step_);
sw.verticalScrollTo(verticalScrollBar.position);
});
verticalScrollBar.addEventListener("scrolltopreviouspage", {
verticalScrollBar.setPosition(verticalScrollBar.position - verticalScrollBar.step_);
sw.verticalScrollTo(verticalScrollBar.position);
});
horizontalScrollBar.addEventListener("scrolltoposition", (Event event) {
horizontalScrollBar.setPosition(event.intValue);
sw.horizontalScrollTo(horizontalScrollBar.position);
});
verticalScrollBar.addEventListener("scrolltoposition", (Event event) {
verticalScrollBar.setPosition(event.intValue);
sw.verticalScrollTo(verticalScrollBar.position);
});
horizontalScrollBar.addEventListener("scrolltrack", (Event event) {
horizontalScrollBar.setPosition(event.intValue);
sw.horizontalScrollTo(horizontalScrollBar.position);
});
verticalScrollBar.addEventListener("scrolltrack", (Event event) {
verticalScrollBar.setPosition(event.intValue);
});
super(parent); super(parent);
} }
@ -2402,10 +2443,14 @@ abstract class ScrollbarBase : Widget {
/// ///
void setViewableArea(int a) { void setViewableArea(int a) {
viewableArea_ = a; viewableArea_ = a;
version(custom_widgets)
redraw();
} }
/// ///
void setMax(int a) { void setMax(int a) {
max_ = a; max_ = a;
version(custom_widgets)
redraw();
} }
/// ///
int max() { int max() {
@ -2413,7 +2458,15 @@ abstract class ScrollbarBase : Widget {
} }
/// ///
void setPosition(int a) { void setPosition(int a) {
if(a == int.max)
a = max;
position_ = max ? a : 0; position_ = max ? a : 0;
if(position_ + viewableArea_ > max)
position_ = max - viewableArea_;
if(position_ < 0)
position_ = 0;
version(custom_widgets)
redraw();
} }
/// ///
int position() { int position() {
@ -2428,6 +2481,7 @@ abstract class ScrollbarBase : Widget {
return step_; return step_;
} }
// FIXME: remove this.... maybe
protected void informProgramThatUserChangedPosition(int n) { protected void informProgramThatUserChangedPosition(int n) {
position_ = n; position_ = n;
auto evt = new Event(EventType.change, this); auto evt = new Event(EventType.change, this);
@ -2561,6 +2615,8 @@ class MouseTrackingWidget : Widget {
redraw(); redraw();
}); });
int lpx, lpy;
addEventListener(EventType.mousemove, (Event event) { addEventListener(EventType.mousemove, (Event event) {
auto oh = hovering; auto oh = hovering;
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) {
@ -2589,9 +2645,14 @@ class MouseTrackingWidget : Widget {
if(positionY < 0) if(positionY < 0)
positionY = 0; positionY = 0;
if(positionX != lpx || positionY != lpy) {
auto evt = new Event(EventType.change, this); auto evt = new Event(EventType.change, this);
evt.sendDirectly(); evt.sendDirectly();
lpx = positionX;
lpy = positionY;
}
redraw(); redraw();
}); });
} }
@ -2608,8 +2669,8 @@ class MouseTrackingWidget : Widget {
} }
} }
version(custom_widgets) //version(custom_widgets)
private //private
class HorizontalScrollbar : ScrollbarBase { class HorizontalScrollbar : ScrollbarBase {
version(custom_widgets) { version(custom_widgets) {
@ -2644,6 +2705,10 @@ class HorizontalScrollbar : ScrollbarBase {
info.nMax = max; info.nMax = max;
info.fMask = SIF_RANGE; info.fMask = SIF_RANGE;
SetScrollInfo(hwnd, SB_CTL, &info, true); SetScrollInfo(hwnd, SB_CTL, &info, true);
} else version(custom_widgets) {
thumb.positionX = thumbPosition;
thumb.thumbWidth = thumbSize;
thumb.redraw();
} }
} }
@ -2676,11 +2741,19 @@ class HorizontalScrollbar : ScrollbarBase {
auto rightButton = new ArrowButton(ArrowDirection.right, vl); auto rightButton = new ArrowButton(ArrowDirection.right, vl);
rightButton.setClickRepeat(scrollClickRepeatInterval); rightButton.setClickRepeat(scrollClickRepeatInterval);
leftButton.tabStop = false;
rightButton.tabStop = false;
thumb.tabStop = false;
leftButton.addEventListener(EventType.triggered, () { leftButton.addEventListener(EventType.triggered, () {
informProgramThatUserChangedPosition(position - step()); auto ev = new Event("scrolltopreviousline", this);
ev.dispatch();
//informProgramThatUserChangedPosition(position - step());
}); });
rightButton.addEventListener(EventType.triggered, () { rightButton.addEventListener(EventType.triggered, () {
informProgramThatUserChangedPosition(position + step()); auto ev = new Event("scrolltonextline", this);
ev.dispatch();
//informProgramThatUserChangedPosition(position + step());
}); });
thumb.thumbWidth = this.minWidth; thumb.thumbWidth = this.minWidth;
@ -2688,7 +2761,11 @@ class HorizontalScrollbar : ScrollbarBase {
thumb.addEventListener(EventType.change, () { thumb.addEventListener(EventType.change, () {
auto sx = thumb.positionX * max() / thumb.width; auto sx = thumb.positionX * max() / thumb.width;
informProgramThatUserChangedPosition(sx); //informProgramThatUserChangedPosition(sx);
auto ev = new Event("scrolltoposition", this);
ev.intValue = sx;
ev.dispatch();
}); });
} }
} }
@ -2698,8 +2775,8 @@ class HorizontalScrollbar : ScrollbarBase {
override int minWidth() { return 48; } override int minWidth() { return 48; }
} }
version(custom_widgets) //version(custom_widgets)
private //private
class VerticalScrollbar : ScrollbarBase { class VerticalScrollbar : ScrollbarBase {
version(custom_widgets) { version(custom_widgets) {
@ -2734,6 +2811,10 @@ class VerticalScrollbar : ScrollbarBase {
info.nMax = max; info.nMax = max;
info.fMask = SIF_RANGE; info.fMask = SIF_RANGE;
SetScrollInfo(hwnd, SB_CTL, &info, true); SetScrollInfo(hwnd, SB_CTL, &info, true);
} else version(custom_widgets) {
thumb.positionY = thumbPosition;
thumb.thumbHeight = thumbSize;
thumb.redraw();
} }
} }
@ -2767,10 +2848,14 @@ class VerticalScrollbar : ScrollbarBase {
downButton.setClickRepeat(scrollClickRepeatInterval); downButton.setClickRepeat(scrollClickRepeatInterval);
upButton.addEventListener(EventType.triggered, () { upButton.addEventListener(EventType.triggered, () {
informProgramThatUserChangedPosition(position - step()); auto ev = new Event("scrolltopreviousline", this);
ev.dispatch();
//informProgramThatUserChangedPosition(position - step());
}); });
downButton.addEventListener(EventType.triggered, () { downButton.addEventListener(EventType.triggered, () {
informProgramThatUserChangedPosition(position + step()); auto ev = new Event("scrolltonextline", this);
ev.dispatch();
//informProgramThatUserChangedPosition(position + step());
}); });
thumb.thumbWidth = this.minWidth; thumb.thumbWidth = this.minWidth;
@ -2779,8 +2864,16 @@ class VerticalScrollbar : ScrollbarBase {
thumb.addEventListener(EventType.change, () { thumb.addEventListener(EventType.change, () {
auto sy = thumb.positionY * max() / thumb.height; auto sy = thumb.positionY * max() / thumb.height;
informProgramThatUserChangedPosition(sy); auto ev = new Event("scrolltoposition", this);
ev.intValue = sy;
ev.dispatch();
//informProgramThatUserChangedPosition(sy);
}); });
upButton.tabStop = false;
downButton.tabStop = false;
thumb.tabStop = false;
} }
} }
@ -3302,6 +3395,138 @@ class HorizontalLayout : Layout {
} }
/++
A widget that takes your widget, puts scroll bars around it, and sends
messages to it when the user scrolls. Unlike [ScrollableWidget], it makes
no effort to automatically scroll or clip its child widgets - it just sends
the messages.
+/
class ScrollMessageWidget : Widget {
this(Widget parent = null) {
super(parent);
container = new Widget(this);
hsb = new HorizontalScrollbar(this);
vsb = new VerticalScrollbar(this);
hsb.addEventListener("scrolltonextline", {
hsb.setPosition(hsb.position + 1);
notify();
});
hsb.addEventListener("scrolltopreviousline", {
hsb.setPosition(hsb.position - 1);
notify();
});
vsb.addEventListener("scrolltonextline", {
vsb.setPosition(vsb.position + 1);
notify();
});
vsb.addEventListener("scrolltopreviousline", {
vsb.setPosition(vsb.position - 1);
notify();
});
hsb.addEventListener("scrolltonextpage", {
hsb.setPosition(hsb.position + hsb.step_);
notify();
});
hsb.addEventListener("scrolltopreviouspage", {
hsb.setPosition(hsb.position - hsb.step_);
notify();
});
vsb.addEventListener("scrolltonextpage", {
vsb.setPosition(vsb.position + vsb.step_);
notify();
});
vsb.addEventListener("scrolltopreviouspage", {
vsb.setPosition(vsb.position - vsb.step_);
notify();
});
hsb.addEventListener("scrolltoposition", (Event event) {
hsb.setPosition(event.intValue);
notify();
});
vsb.addEventListener("scrolltoposition", (Event event) {
vsb.setPosition(event.intValue);
notify();
});
tabStop = false;
container.tabStop = false;
magic = true;
}
void notify() {
auto event = new Event("scroll", this);
event.dispatch();
}
///
Point position() {
return Point(hsb.position, vsb.position);
}
///
void setPosition(int x, int y) {
hsb.setPosition(x);
vsb.setPosition(y);
}
///
void setPageSize(int unitsX, int unitsY) {
hsb.setStep(unitsX);
vsb.setStep(unitsY);
}
///
void setTotalArea(int width, int height) {
hsb.setMax(width);
vsb.setMax(height);
}
///
void setViewableArea(int width, int height) {
hsb.setViewableArea(width);
vsb.setViewableArea(height);
}
private bool magic;
override void addChild(Widget w, int position = int.max) {
if(magic)
container.addChild(w, position);
else
super.addChild(w, position);
}
override void recomputeChildLayout() {
if(hsb is null || vsb is null || container is null) return;
registerMovement();
hsb.height = 16; // FIXME? are tese 16s sane?
hsb.x = 0;
hsb.y = this.height - hsb.height;
hsb.width = this.width - 16;
hsb.recomputeChildLayout();
vsb.width = 16; // FIXME?
vsb.x = this.width - vsb.width;
vsb.y = 0;
vsb.height = this.height - 16;
vsb.recomputeChildLayout();
container.x = 0;
container.y = 0;
container.width = this.width - vsb.width;
container.height = this.height - hsb.height;
container.recomputeChildLayout();
}
HorizontalScrollbar hsb;
VerticalScrollbar vsb;
Widget container;
}
/++ /++
Bypasses automatic layout for its children, using manual positioning and sizing only. Bypasses automatic layout for its children, using manual positioning and sizing only.
While you need to manually position them, you must ensure they are inside the StaticLayout's While you need to manually position them, you must ensure they are inside the StaticLayout's
@ -3471,6 +3696,97 @@ class Window : Widget {
if(hwnd !is this.win.impl.hwnd) if(hwnd !is this.win.impl.hwnd)
return 1; // we don't care... return 1; // we don't care...
switch(msg) { switch(msg) {
case WM_VSCROLL, WM_HSCROLL:
auto pos = HIWORD(wParam);
auto m = LOWORD(wParam);
auto scrollbarHwnd = cast(HWND) lParam;
if(auto widgetp = scrollbarHwnd in Widget.nativeMapping) {
//auto smw = cast(ScrollMessageWidget) widgetp.parent;
switch(m) {
/+
// I don't think those messages are ever actually sent normally by the widget itself,
// they are more used for the keyboard interface. methinks.
case SB_BOTTOM:
import std.stdio; writeln("end");
auto event = new Event("scrolltoend", *widgetp);
event.dispatch();
//if(!event.defaultPrevented)
break;
case SB_TOP:
import std.stdio; writeln("top");
auto event = new Event("scrolltobeginning", *widgetp);
event.dispatch();
break;
case SB_ENDSCROLL:
// idk
break;
+/
case SB_LINEDOWN:
auto event = new Event("scrolltonextline", *widgetp);
event.dispatch();
break;
case SB_LINEUP:
auto event = new Event("scrolltopreviousline", *widgetp);
event.dispatch();
break;
case SB_PAGEDOWN:
auto event = new Event("scrolltonextpage", *widgetp);
event.dispatch();
break;
case SB_PAGEUP:
auto event = new Event("scrolltopreviouspage", *widgetp);
event.dispatch();
break;
case SB_THUMBPOSITION:
auto event = new Event("scrolltoposition", *widgetp);
event.intValue = pos;
event.dispatch();
break;
case SB_THUMBTRACK:
auto event = new Event("scrolltrack", *widgetp);
event.intValue = pos;
event.dispatch();
/+
if(m == SB_THUMBTRACK) {
// the event loop doesn't seem to carry on with a requested redraw..
// so we request it to get our dirty bit set...
redraw();
// then we need to immediately actually redraw it too for instant feedback to user
actualRedraw();
}
+/
break;
default:
}
} else {
return 1;
}
break;
case WM_CONTEXTMENU:
auto hwndFrom = cast(HWND) wParam;
/+
auto xPos = GET_X_LPARAM(lParam);
auto yPos = GET_Y_LPARAM(lParam);
if(auto widgetp = hwndFrom in Widget.nativeMapping) {
// FIXME: translate screen coordinates to widget coordinates
auto menu = (*widgetp).contextMenu(xPos, yPos);
if(menu is null)
return 1; // pass it on
//TrackContextMenuEx
}
+/
break;
case WM_NOTIFY: case WM_NOTIFY:
auto hdr = cast(NMHDR*) lParam; auto hdr = cast(NMHDR*) lParam;
auto hwndFrom = hdr.hwndFrom; auto hwndFrom = hdr.hwndFrom;