Hover, scrollbars

This commit is contained in:
Vadim Lopatin 2014-03-25 14:40:47 +04:00
parent 4187da1a2d
commit 03a0844a9f
8 changed files with 126 additions and 54 deletions

View File

@ -63,10 +63,17 @@ extern (C) int UIAppMain(string[] args) {
hlayout.backgroundColor = 0x8080C0;
layout.addChild(hlayout);
LinearLayout vlayoutgroup = new HorizontalLayout();
LinearLayout vlayout = new VerticalLayout();
vlayout.addChild((new TextWidget()).text("VLayout line 1").textColor(0x40FF4000)); //
vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40FFFF00));
layout.addChild(vlayout);
vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40FF8000));
vlayout.addChild((new TextWidget()).text("VLayout line 2").textColor(0x40008000));
vlayout.layoutWidth(FILL_PARENT);
vlayoutgroup.addChild(vlayout);
vlayoutgroup.layoutWidth(FILL_PARENT);
ScrollBar vsb = new ScrollBar("vscroll", Orientation.Vertical);
vlayoutgroup.addChild(vsb);
layout.addChild(vlayoutgroup);
ScrollBar sb = new ScrollBar("hscroll", Orientation.Horizontal);
layout.addChild(sb.layoutHeight(WRAP_CONTENT).layoutWidth(FILL_PARENT));

Binary file not shown.

Before

Width:  |  Height:  |  Size: 670 B

After

Width:  |  Height:  |  Size: 716 B

View File

@ -7,9 +7,10 @@ enum MouseAction : ubyte {
ButtonDown, // button is down
ButtonUp, // button is up
Move, // mouse pointer is moving
FocusIn, // pointer moved outside of widget while button was down
FocusOut, // pointer is back inside widget while button is down after FocusIn
FocusIn, // pointer is back inside widget while button is down after FocusOut
FocusOut, // pointer moved outside of widget while button was down (if handler returns true, Move events will be sent even while pointer is outside widget)
Wheel, // scroll wheel movement
//Hover, // pointer entered widget which while button was not down (return true to track Hover state)
Leave // pointer left widget which has before processed Move message, while button was not down
}

View File

@ -122,6 +122,7 @@ class Window {
protected Widget _mouseCaptureWidget;
protected ushort _mouseCaptureButtons;
protected bool _mouseCaptureFocusedOut;
protected bool _mouseCaptureFocusedOutTrackMovements;
protected bool dispatchCancel(MouseEvent event) {
event.changeAction(MouseAction.Cancel);
@ -157,6 +158,9 @@ class Window {
event.changeAction(MouseAction.FocusOut);
_mouseCaptureFocusedOut = true;
_mouseCaptureButtons = event.flags & (MouseFlag.LButton|MouseFlag.RButton|MouseFlag.MButton);
_mouseCaptureFocusedOutTrackMovements = _mouseCaptureWidget.onMouseEvent(event);
return true;
} else if (_mouseCaptureFocusedOutTrackMovements) {
return _mouseCaptureWidget.onMouseEvent(event);
}
return true;
@ -189,27 +193,35 @@ class Window {
}
return res;
}
bool processed = false;
if (event.action == MouseAction.Move && _mouseTrackingWidget !is null) {
if (!_mouseTrackingWidget.isPointInside(event.x, event.y)) {
// send Leave message
MouseEvent leaveEvent = new MouseEvent(event);
leaveEvent.changeAction(MouseAction.Leave);
_mouseCaptureWidget.onMouseEvent(event);
_mouseTrackingWidget.onMouseEvent(leaveEvent);
// stop tracking
_mouseTrackingWidget = null;
processed = true;
}
}
if (!res) {
res = dispatchMouseEvent(_mainWidget, event);
}
return res;
return res || processed;
}
/// checks content widgets for necessary redraw and/or layout
protected void checkUpdateNeeded(Widget root, ref bool needDraw, ref bool needLayout, ref bool animationActive) {
if (!root.visibility == Visibility.Visible)
return;
needDraw = root.needDraw || needDraw;
needLayout = root.needLayout || needLayout;
if (!needLayout) {
needLayout = root.needLayout || needLayout;
if (needLayout) {
Log.d("need layout: ", root.id);
}
}
animationActive = root.animating || animationActive;
for (int i = 0; i < root.childCount; i++)
checkUpdateNeeded(root.child(i), needDraw, needLayout, animationActive);

View File

@ -201,7 +201,6 @@ class Win32Window : Window {
version (USE_OPENGL) {
private void paintUsingOpenGL() {
Log.d("paintUsingOpenGL()");
// hack to stop infinite WM_PAINT loop
PAINTSTRUCT ps;
HDC hdc2 = BeginPaint(_hwnd, &ps);
@ -223,11 +222,9 @@ class Win32Window : Window {
float r = ((_backgroundColor >> 16) & 255) / 255.0f;
float g = ((_backgroundColor >> 8) & 255) / 255.0f;
float b = ((_backgroundColor >> 0) & 255) / 255.0f;
Log.d("paintUsingOpenGL() - clearing buffer");
glClearColor(r, g, b, a);
glClear(GL_COLOR_BUFFER_BIT);
Log.d("paintUsingOpenGL() - creating drawbuf");
GLDrawBuf buf = new GLDrawBuf(_dx, _dy, false);
buf.beforeDrawing();
static if (false) {
@ -244,9 +241,7 @@ class Win32Window : Window {
} else {
onDraw(buf);
}
Log.d("paintUsingOpenGL() - calling buf.afterDrawing");
buf.afterDrawing();
Log.d("onPaint() end drawing opengl");
SwapBuffers(hdc);
wglMakeCurrent(hdc, null);
}

View File

@ -117,6 +117,7 @@ class ImageButton : ImageWidget {
super(ID);
styleId = "BUTTON";
_drawableId = drawableId;
trackHover = true;
}
}
@ -127,6 +128,7 @@ class Button : Widget {
this(string ID = null) {
super(ID);
styleId = "BUTTON";
trackHover = true;
}
override void measure(int parentWidth, int parentHeight) {
@ -169,6 +171,7 @@ class ScrollBar : WidgetGroup, OnClickHandler {
this(string ID) {
super(ID);
styleId = "PAGE_SCROLL";
trackHover = true;
}
}
@ -177,8 +180,10 @@ class ScrollBar : WidgetGroup, OnClickHandler {
int _dragStartPosition;
bool _dragging;
Rect _dragStartRect;
this(string resourceId) {
super("SLIDER", resourceId);
trackHover = true;
}
/// process mouse event; return true if event is processed by widget.
@ -193,6 +198,9 @@ class ScrollBar : WidgetGroup, OnClickHandler {
_dragStartRect = _pos;
return true;
}
if (event.action == MouseAction.FocusOut && _dragging) {
return true;
}
if (event.action == MouseAction.Move && _dragging) {
int delta = _orientation == Orientation.Vertical ? event.y - _dragStart.y : event.x - _dragStart.x;
Rect rc = _dragStartRect;
@ -205,7 +213,7 @@ class ScrollBar : WidgetGroup, OnClickHandler {
rc.top = _scrollArea.top;
rc.bottom = _scrollArea.top + _dragStartRect.height;
} else if (rc.bottom > _scrollArea.bottom) {
rc.top = _scrollArea.top - _dragStartRect.height;
rc.top = _scrollArea.bottom - _dragStartRect.height;
rc.bottom = _scrollArea.bottom;
}
offset = rc.top - _scrollArea.top;
@ -223,7 +231,8 @@ class ScrollBar : WidgetGroup, OnClickHandler {
offset = rc.left - _scrollArea.left;
space = _scrollArea.width - rc.width;
}
_pos = rc;
layoutButtons(rc);
//_pos = rc;
int position = space > 0 ? _minValue + offset * (_maxValue - _minValue - _pageSize) / space : 0;
invalidate();
onIndicatorDragging(_dragStartPosition, position);
@ -237,6 +246,18 @@ class ScrollBar : WidgetGroup, OnClickHandler {
}
return true;
}
if (event.action == MouseAction.Move && trackHover) {
if (!(state & State.Hover)) {
Log.d("Hover ", id);
setState(State.Hover);
}
return true;
}
if ((event.action == MouseAction.Leave || event.action == MouseAction.Cancel) && trackHover) {
Log.d("Leave ", id);
resetState(State.Hover);
return true;
}
if (event.action == MouseAction.Cancel) {
Log.d("SliderButton.onMouseEvent event.action == MouseAction.Cancel");
resetState(State.Pressed);
@ -347,6 +368,47 @@ class ScrollBar : WidgetGroup, OnClickHandler {
measuredContent(parentWidth, parentHeight, sz.x, sz.y);
}
protected void layoutButtons(Rect irc) {
Rect r;
if (_orientation == Orientation.Vertical) {
_indicator.layout(irc);
if (_scrollArea.top < irc.top) {
r = _scrollArea;
r.bottom = irc.top;
_pageUp.layout(r);
_pageUp.visibility = Visibility.Visible;
} else {
_pageUp.visibility = Visibility.Invisible;
}
if (_scrollArea.bottom > irc.bottom) {
r = _scrollArea;
r.top = irc.bottom;
_pageDown.layout(r);
_pageDown.visibility = Visibility.Visible;
} else {
_pageDown.visibility = Visibility.Invisible;
}
} else {
_indicator.layout(irc);
if (_scrollArea.left < irc.left) {
r = _scrollArea;
r.right = irc.left;
_pageUp.layout(r);
_pageUp.visibility = Visibility.Visible;
} else {
_pageUp.visibility = Visibility.Invisible;
}
if (_scrollArea.right > irc.right) {
r = _scrollArea;
r.left = irc.right;
_pageDown.layout(r);
_pageDown.visibility = Visibility.Visible;
} else {
_pageDown.visibility = Visibility.Invisible;
}
}
}
override void layout(Rect rc) {
applyMargins(rc);
applyPadding(rc);
@ -372,23 +434,7 @@ class ScrollBar : WidgetGroup, OnClickHandler {
Rect irc = r;
irc.top += spaceBackSize;
irc.bottom -= spaceForwardSize;
_indicator.layout(irc);
if (_scrollArea.top < irc.top) {
r = _scrollArea;
r.bottom = irc.top;
_pageUp.layout(r);
_pageUp.visibility = Visibility.Visible;
} else {
_pageUp.visibility = Visibility.Invisible;
}
if (_scrollArea.bottom > irc.bottom) {
r = _scrollArea;
r.top = irc.bottom;
_pageDown.layout(r);
_pageDown.visibility = Visibility.Visible;
} else {
_pageDown.visibility = Visibility.Invisible;
}
layoutButtons(irc);
} else {
// horizontal
int backbtnpos = rc.left + _btnSize;
@ -409,23 +455,7 @@ class ScrollBar : WidgetGroup, OnClickHandler {
Rect irc = r;
irc.left += spaceBackSize;
irc.right -= spaceForwardSize;
_indicator.layout(irc);
if (_scrollArea.left < irc.left) {
r = _scrollArea;
r.right = irc.left;
_pageUp.layout(r);
_pageUp.visibility = Visibility.Visible;
} else {
_pageUp.visibility = Visibility.Invisible;
}
if (_scrollArea.right > irc.right) {
r = _scrollArea;
r.left = irc.right;
_pageDown.layout(r);
_pageDown.visibility = Visibility.Visible;
} else {
_pageDown.visibility = Visibility.Invisible;
}
layoutButtons(irc);
}
_pos = rc;
_needLayout = false;

View File

@ -25,6 +25,7 @@ enum State : uint {
Pressed = 1,
Focused = 2,
Disabled = 4,
Hover = 8, // mouse pointer is over control, buttons not pressed
}
enum Align : ubyte {
@ -77,7 +78,7 @@ class Style {
protected FontFamily _fontFamily = FontFamily.Unspecified;
protected ushort _fontSize = FONT_SIZE_UNSPECIFIED;
protected ushort _fontWeight = FONT_WEIGHT_UNSPECIFIED;
protected uint _backgroundColor = COLOR_TRANSPARENT;
protected uint _backgroundColor = COLOR_UNSPECIFIED;
protected uint _textColor = COLOR_UNSPECIFIED;
protected string _fontFace;
protected string _backgroundImageId;
@ -621,6 +622,7 @@ Theme createDefaultTheme() {
button.createState(State.Disabled, State.Disabled).backgroundImageId("btn_default_small_normal_disable");
button.createState(State.Pressed, State.Pressed).backgroundImageId("btn_default_small_pressed");
button.createState(State.Focused, State.Focused).backgroundImageId("btn_default_small_selected");
button.createState(State.Hover, State.Hover).backgroundImageId("btn_default_small_normal_hover");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_UP, "scrollbar_btn_up");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_DOWN, "scrollbar_btn_down");
res.setCustomDrawable(ATTR_SCROLLBAR_BUTTON_LEFT, "scrollbar_btn_left");
@ -629,11 +631,12 @@ Theme createDefaultTheme() {
res.setCustomDrawable(ATTR_SCROLLBAR_INDICATOR_HORIZONTAL, "scrollbar_indicator_horizontal");
Style scrollbar = res.createSubstyle("SCROLLBAR");
scrollbar.backgroundColor(0xC0808080);
Style scrollbarButton = button.createSubstyle("SCROLLBAR_BUTTON");
scrollbar.backgroundColor(0xC0C0C0C0);
Style scrollbarSlider = res.createSubstyle("SLIDER");
Style scrollbarPage = res.createSubstyle("PAGE_SCROLL").backgroundColor(0xFFFFFFFF); // transparent
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404040);
Style scrollbarPage = res.createSubstyle("PAGE_SCROLL").backgroundColor(0xFFFFFFFF);
scrollbarPage.createState(State.Pressed, State.Pressed).backgroundColor(0xC0404080);
scrollbarPage.createState(State.Hover, State.Hover).backgroundColor(0xF0404080);
//res.dumpStats();
return res;

View File

@ -67,6 +67,14 @@ class Widget {
/// window (to be used for top level widgets only!)
protected Window _window;
/// does widget need to track mouse Hover
protected bool _trackHover;
/// mouse movement processing flag (when true, widget will change Hover state while mouse is moving)
@property bool trackHover() const { return _trackHover; }
/// set new trackHover flag value (when true, widget will change Hover state while mouse is moving)
@property Widget trackHover(bool v) { _trackHover = v; return this; }
//private static int _instanceCount = 0;
/// create widget, with optional id
this(string ID = null) {
@ -277,8 +285,11 @@ class Widget {
@property Visibility visibility() { return _visibility; }
/// sets widget visibility (Visible, Invisible, Gone)
@property Widget visibility(Visibility visible) {
_visibility = visible;
requestLayout();
if (_visibility != visible) {
if ((_visibility == Visibility.Gone) || (visible == Visibility.Gone))
_visibility = visible;
requestLayout();
}
return this;
}
@ -305,12 +316,25 @@ class Widget {
}
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
resetState(State.Pressed);
resetState(State.Hover);
return true;
}
if (event.action == MouseAction.FocusIn) {
setState(State.Pressed);
return true;
}
if (event.action == MouseAction.Move && trackHover) {
if (!(state & State.Hover)) {
Log.d("Hover ", id);
setState(State.Hover);
}
return true;
}
if (event.action == MouseAction.Leave && trackHover) {
Log.d("Leave ", id);
resetState(State.Hover);
return true;
}
}
return false;
}