popup support

This commit is contained in:
Vadim Lopatin 2014-04-14 15:10:32 +04:00
parent 85c3dd0091
commit 61b8fe91d4
8 changed files with 169 additions and 37 deletions

View File

@ -50,7 +50,11 @@ extern (C) int UIAppMain(string[] args) {
static if (true) {
VerticalLayout contentLayout = new VerticalLayout();
MenuItem mainMenuItems = new MenuItem();
mainMenuItems.add(new Action(1, "File"d));
MenuItem fileItem = new MenuItem(new Action(1, "File"d));
fileItem.add(new Action(10, "Open..."d));
fileItem.add(new Action(11, "Save..."d));
fileItem.add(new Action(12, "Exit"d));
mainMenuItems.add(fileItem);
mainMenuItems.add(new Action(2, "Edit"d));
mainMenuItems.add(new Action(3, "Window"d));
mainMenuItems.add(new Action(4, "Help"d));
@ -66,7 +70,7 @@ extern (C) int UIAppMain(string[] args) {
layout.addChild((new TextWidget()).textColor(0x40FF4000).text("Text widget"));
layout.addChild((new Button("BTN1")).textResource("EXIT")); //.textColor(0x40FF4000)
static if (false) {
LinearLayout hlayout = new HorizontalLayout();
@ -107,18 +111,21 @@ extern (C) int UIAppMain(string[] args) {
layout.childById("BTN2").onClickListener(delegate (Widget w) { Log.d("onClick ", w.id); return true; });
layout.childById("BTN3").onClickListener(delegate (Widget w) { Log.d("onClick ", w.id); return true; });
}
layout.layoutHeight(FILL_PARENT).layoutWidth(FILL_PARENT);
tabs.addTab(layout, "Tab 1"d);
ListWidget list = new ListWidget("tab2", Orientation.Vertical);
WidgetListAdapter listAdapter = new WidgetListAdapter();
for (int i = 0; i < 3000; i++)
listAdapter.widgets.add((new TextWidget()).text("List item "d ~ to!dstring(i)));
list.ownAdapter = listAdapter;
list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
tabs.addTab(list, "Lists"d);
static if (false) {
ListWidget list = new ListWidget("tab2", Orientation.Vertical);
WidgetListAdapter listAdapter = new WidgetListAdapter();
for (int i = 0; i < 3000; i++)
listAdapter.widgets.add((new TextWidget()).text("List item "d ~ to!dstring(i)));
list.ownAdapter = listAdapter;
list.layoutWidth(FILL_PARENT).layoutHeight(FILL_PARENT);
tabs.addTab(list, "Lists"d);
}
tabs.addTab((new TextWidget()).id("tab3").textColor(0x00802000).text("Tab 3 contents"), "Tab 3"d);
tabs.addTab((new TextWidget()).id("tab4").textColor(0x00802000).text("Tab 4 contents some long string"), "Tab 4"d);

View File

@ -1,6 +1,7 @@
module dlangui.core.events;
import dlangui.core.i18n;
private import dlangui.widgets.widget;
import std.conv;
@ -129,6 +130,7 @@ class MouseEvent {
protected short _y;
protected ushort _flags;
protected short _wheelDelta;
protected Widget _trackingWidget;
protected ButtonDetails _lbutton;
protected ButtonDetails _mbutton;
protected ButtonDetails _rbutton;
@ -142,6 +144,12 @@ class MouseEvent {
@property short wheelDelta() { return _wheelDelta; }
@property short x() { return _x; }
@property short y() { return _y; }
/// get event tracking widget to override
@property Widget trackingWidget() { return _trackingWidget; }
/// override mouse tracking widget
void track(Widget w) {
_trackingWidget = w;
}
this (MouseEvent e) {
_eventTimestamp = e._eventTimestamp;
_action = e._action;

View File

@ -46,6 +46,24 @@ struct Rect {
@property bool empty() {
return right <= left || bottom <= top;
}
void moveBy(int deltax, int deltay) {
left += deltax;
right += deltax;
top += deltay;
bottom += deltay;
}
/// moves this rect to fit rc bounds, retaining the same size
void moveToFit(ref Rect rc) {
if (right > rc.right)
moveBy(rc.right - right, 0);
if (bottom > rc.bottom)
moveBy(0, rc.bottom - bottom);
if (left < rc.left)
moveBy(rc.left - left, 0);
if (top < rc.top)
moveBy(0, rc.top - top);
}
/// updates this rect to intersection with rc, returns true if result is non empty
bool intersect(Rect rc) {
if (left < rc.left)

View File

@ -61,9 +61,10 @@ class Window {
protected PopupWidget[] _popups;
/// show new popup
PopupWidget showPopup(Widget content) {
PopupWidget showPopup(Widget content, Widget anchor = null, uint alignment = PopupAlign.Center) {
PopupWidget res = new PopupWidget(content, this);
res.anchor.widget = _mainWidget;
res.anchor.widget = anchor !is null ? anchor : _mainWidget;
res.anchor.alignment = alignment;
_popups ~= res;
if (_mainWidget !is null)
_mainWidget.requestLayout();
@ -169,6 +170,12 @@ class Window {
// override if necessary
}
protected void setCaptureWidget(Widget w, MouseEvent event) {
_mouseCaptureWidget = w;
_mouseCaptureButtons = event.flags & (MouseFlag.LButton|MouseFlag.RButton|MouseFlag.MButton);
}
protected bool dispatchMouseEvent(Widget root, MouseEvent event) {
// only route mouse events to visible widgets
if (root.visibility != Visibility.Visible)
@ -182,12 +189,11 @@ class Window {
return true;
}
// if not processed by children, offer event to root
if (root.onMouseEvent(event)) {
if (sendAndCheckOverride(root, event)) {
Log.d("MouseEvent is processed");
if (event.action == MouseAction.ButtonDown && _mouseCaptureWidget is null) {
Log.d("Setting active widget");
_mouseCaptureWidget = root;
_mouseCaptureButtons = event.flags & (MouseFlag.LButton|MouseFlag.RButton|MouseFlag.MButton);
setCaptureWidget(root, event);
} else if (event.action == MouseAction.Move) {
addTracking(root);
}
@ -256,6 +262,14 @@ class Window {
return res;
}
protected bool sendAndCheckOverride(Widget widget, MouseEvent event) {
bool res = widget.onMouseEvent(event);
if (event.trackingWidget !is null && _mouseCaptureWidget !is event.trackingWidget) {
setCaptureWidget(event.trackingWidget, event);
}
return res;
}
/// dispatch mouse event to window content widgets
bool dispatchMouseEvent(MouseEvent event) {
// ignore events if there is no root
@ -282,11 +296,11 @@ class Window {
event.changeAction(MouseAction.FocusOut);
_mouseCaptureFocusedOut = true;
_mouseCaptureButtons = currentButtons;
_mouseCaptureFocusedOutTrackMovements = _mouseCaptureWidget.onMouseEvent(event);
_mouseCaptureFocusedOutTrackMovements = sendAndCheckOverride(_mouseCaptureWidget, event);
return true;
} else if (_mouseCaptureFocusedOutTrackMovements) {
// pointer is outside, but we still need to track pointer
return _mouseCaptureWidget.onMouseEvent(event);
return sendAndCheckOverride(_mouseCaptureWidget, event);
}
// don't forward message
return true;
@ -298,7 +312,7 @@ class Window {
return dispatchCancel(event);
event.changeAction(MouseAction.FocusIn); // back in after focus out
}
return _mouseCaptureWidget.onMouseEvent(event);
return sendAndCheckOverride(_mouseCaptureWidget, event);
}
} else if (event.action == MouseAction.Leave) {
if (!_mouseCaptureFocusedOut) {
@ -306,12 +320,12 @@ class Window {
event.changeAction(MouseAction.FocusOut);
_mouseCaptureFocusedOut = true;
_mouseCaptureButtons = event.flags & (MouseFlag.LButton|MouseFlag.RButton|MouseFlag.MButton);
return _mouseCaptureWidget.onMouseEvent(event);
return sendAndCheckOverride(_mouseCaptureWidget, event);
}
return true;
}
// other messages
res = _mouseCaptureWidget.onMouseEvent(event);
res = sendAndCheckOverride(_mouseCaptureWidget, event);
if (!currentButtons) {
// usable capturing - no more buttons pressed
Log.d("unsetting active widget");
@ -324,6 +338,10 @@ class Window {
processed = checkRemoveTracking(event);
}
if (!res) {
foreach(p; _popups) {
if (dispatchMouseEvent(p, event))
return true;
}
res = dispatchMouseEvent(_mainWidget, event);
}
return res || processed;

View File

@ -211,6 +211,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
sz.x += w.measuredWidth;
}
}
_needScrollbar = false;
if (_orientation == Orientation.Vertical) {
if (pheight != SIZE_UNSPECIFIED && sz.y > pheight) {
// need scrollbar
@ -329,6 +330,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
}
_pos = rc;
Rect parentrc = rc;
applyMargins(rc);
applyPadding(rc);
@ -337,7 +339,7 @@ class ListWidget : WidgetGroup, OnScrollHandler {
// measure again if client size has been changed
if (_lastMeasureWidth != rc.width || _lastMeasureHeight != rc.height)
measure(rc.width, rc.height);
measure(parentrc.width, parentrc.height);
// layout scrollbar
if (_needScrollbar) {

View File

@ -4,6 +4,7 @@ import dlangui.core.events;
import dlangui.widgets.controls;
import dlangui.widgets.layouts;
import dlangui.widgets.lists;
import dlangui.widgets.popup;
class MenuItem {
protected bool _checkable;
@ -55,9 +56,18 @@ class MenuItem {
}
}
interface MenuItemWidgetHandler {
bool onItemMouseDown(MenuItemWidget itemWidget, MouseEvent ev);
bool onItemMouseUp(MenuItemWidget itemWidget, MouseEvent ev);
}
class MenuItemWidget : HorizontalLayout {
protected MenuItem _item;
protected TextWidget _label;
protected MenuItemWidgetHandler _handler;
@property MenuItemWidgetHandler handler() { return _handler; }
@property MenuItemWidget handler(MenuItemWidgetHandler h) { _handler = h; return this; }
@property MenuItem item() { return _item; }
this(MenuItem item) {
id="menuitem";
_item = item;
@ -67,21 +77,73 @@ class MenuItemWidget : HorizontalLayout {
addChild(_label);
trackHover = true;
}
/// process mouse event; return true if event is processed by widget.
override bool onMouseEvent(MouseEvent event) {
Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
// support onClick
if (_handler !is null) {
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {
setState(State.Pressed);
_handler.onItemMouseDown(this, event);
return true;
}
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
resetState(State.Pressed);
_handler.onItemMouseDown(this, event);
return true;
}
/*
if (event.action == MouseAction.ButtonUp && event.button == MouseButton.Left) {
resetState(State.Pressed);
_onClickListener(this);
return true;
}
if (event.action == MouseAction.FocusOut || event.action == MouseAction.Cancel) {
resetState(State.Pressed);
resetState(State.Hovered);
return true;
}
if (event.action == MouseAction.FocusIn) {
setState(State.Pressed);
return true;
}
*/
}
return super.onMouseEvent(event);
}
}
class MainMenu : HorizontalLayout {
class MainMenu : HorizontalLayout, MenuItemWidgetHandler {
protected MenuItem _item;
protected bool onItemClick(Widget w) {
Log.d("onItemClick ", w.id);
override protected bool onItemMouseDown(MenuItemWidget itemWidget, MouseEvent ev) {
PopupMenu popupMenu = new PopupMenu(itemWidget.item);
PopupWidget popup = window.showPopup(popupMenu, itemWidget, PopupAlign.Below);
ev.track(popupMenu);
return true;
}
override protected bool onItemMouseUp(MenuItemWidget itemWidget, MouseEvent ev) {
return true;
}
/*
protected bool onItemClick(Widget w) {
MenuItemWidget itemWidget = cast(MenuItemWidget)w;
Log.d("onItemClick ", w.id);
window.showPopup(new PopupMenu(itemWidget.item), itemWidget, PopupAlign.Below);
return true;
}
*/
this(MenuItem item) {
id = "MAIN_MENU";
styleId = "MAIN_MENU";
_item = item;
for (int i = 0; i < item.subitemCount; i++) {
MenuItemWidget subitem = new MenuItemWidget(item.subitem(i));
subitem.onClickListener = &onItemClick;
//subitem.onClickListener = &onItemClick;
subitem.handler = this;
addChild(subitem);
}
addChild((new Widget()).layoutWidth(FILL_PARENT));

View File

@ -4,9 +4,17 @@ import dlangui.widgets.widget;
import dlangui.widgets.layouts;
import dlangui.platforms.common.platform;
/// popup alignment option flags
enum PopupAlign : uint {
/// center popup around anchor widget center
Center = 1,
/// place popup below anchor widget close to lower bound
Below = 2,
}
struct PopupAnchor {
Widget widget;
Align alignment;
uint alignment = PopupAlign.Center;
}
/// popup widget container
@ -36,24 +44,33 @@ class PopupWidget : LinearLayout {
w = rc.width;
if (h > rc.height)
h = rc.height;
int extraw = rc.width - w;
int extrah = rc.height - h;
Rect anchorrc;
if (anchor.widget !is null)
anchorrc = anchor.widget.pos;
else
anchorrc = rc;
Rect r;
if (anchor.widget !is null)
r = anchor.widget.pos;
else
r = rc;
r.left += extraw / 2;
r.top += extrah / 2;
r.right -= extraw / 2;
r.bottom -= extrah / 2;
Point anchorPt;
if (anchor.alignment & PopupAlign.Center) {
// center around center of anchor widget
r.left = anchorrc.middlex - w / 2;
r.top = anchorrc.middley - h / 2;
} else if (anchor.alignment & PopupAlign.Below) {
r.left = anchorrc.left;
r.top = anchorrc.bottom;
}
r.right = r.left + w;
r.bottom = r.top + h;
r.moveToFit(rc);
super.layout(r);
}
this(Widget content, Window window) {
_window = window;
styleId = "POPUP_MENU";
//styleId = "POPUP_MENU";
addChild(content);
}
}

View File

@ -312,7 +312,7 @@ class Widget {
/// process mouse event; return true if event is processed by widget.
bool onMouseEvent(MouseEvent event) {
Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
//Log.d("onMouseEvent ", id, " ", event.action, " (", event.x, ",", event.y, ")");
// support onClick
if (_onClickListener !is null) {
if (event.action == MouseAction.ButtonDown && event.button == MouseButton.Left) {