diff --git a/dlanguilib.dproj b/dlanguilib.dproj index 2ee2615b..eda50e6e 100644 --- a/dlanguilib.dproj +++ b/dlanguilib.dproj @@ -179,6 +179,7 @@ + diff --git a/src/dlangui/all.d b/src/dlangui/all.d index 65550e01..4b470910 100644 --- a/src/dlangui/all.d +++ b/src/dlangui/all.d @@ -1,14 +1,14 @@ -module dlangui.all; - -public import dlangui.core.logger; -public import dlangui.core.types; -public import dlangui.platforms.common.platform; -public import dlangui.graphics.images; -public import dlangui.widgets.widget; -public import dlangui.widgets.controls; -public import dlangui.widgets.layouts; -public import dlangui.widgets.lists; -public import dlangui.widgets.tabs; -public import dlangui.widgets.menu; -public import dlangui.graphics.fonts; -public import dlangui.core.i18n; +module dlangui.all; + +public import dlangui.core.logger; +public import dlangui.core.types; +public import dlangui.platforms.common.platform; +public import dlangui.graphics.images; +public import dlangui.widgets.widget; +public import dlangui.widgets.controls; +public import dlangui.widgets.layouts; +public import dlangui.widgets.lists; +public import dlangui.widgets.tabs; +public import dlangui.widgets.menu; +public import dlangui.graphics.fonts; +public import dlangui.core.i18n; diff --git a/src/dlangui/core/types.d b/src/dlangui/core/types.d index 2fa909de..37733bf3 100644 --- a/src/dlangui/core/types.d +++ b/src/dlangui/core/types.d @@ -1,99 +1,99 @@ -module dlangui.core.types; - -import std.algorithm; - -struct Point { - int x; - int y; - this(int x0, int y0) { - x = x0; - y = y0; - } -} - -struct Rect { - int left; - int top; - int right; - int bottom; - @property int middlex() { return (left + right) / 2; } - @property int middley() { return (top + bottom) / 2; } - void offset(int dx, int dy) { - left += dx; - right += dx; - top += dy; - bottom += dy; - } - /// for all fields, sets this.field to rc.field if rc.field > this.field - void setMax(Rect rc) { - if (left < rc.left) - left = rc.left; - if (right < rc.right) - right = rc.right; - if (top < rc.top) - top = rc.top; - if (bottom < rc.bottom) - bottom = rc.bottom; - } - @property int width() { return right - left; } - @property int height() { return bottom - top; } - this(int x0, int y0, int x1, int y1) { - left = x0; - top = y0; - right = x1; - bottom = y1; - } - @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) - left = rc.left; - if (top < rc.top) - top = rc.top; - if (right > rc.right) - right = rc.right; - if (bottom > rc.bottom) - bottom = rc.bottom; - return right > left && bottom > top; - } - /// returns true if this rect has nonempty intersection with rc - bool intersects(Rect rc) { - if (rc.left >= right || rc.top >= bottom || rc.right <= left || rc.bottom <= top) - return false; - return true; - } - /// returns true if point is inside of this rectangle - bool isPointInside(Point pt) { - return pt.x >= left && pt.x < right && pt.y >= top && pt.y < bottom; - } - /// returns true if point is inside of this rectangle - bool isPointInside(int x, int y) { - return x >= left && x < right && y >= top && y < bottom; - } -} - -/// character glyph -align(1) +module dlangui.core.types; + +import std.algorithm; + +struct Point { + int x; + int y; + this(int x0, int y0) { + x = x0; + y = y0; + } +} + +struct Rect { + int left; + int top; + int right; + int bottom; + @property int middlex() { return (left + right) / 2; } + @property int middley() { return (top + bottom) / 2; } + void offset(int dx, int dy) { + left += dx; + right += dx; + top += dy; + bottom += dy; + } + /// for all fields, sets this.field to rc.field if rc.field > this.field + void setMax(Rect rc) { + if (left < rc.left) + left = rc.left; + if (right < rc.right) + right = rc.right; + if (top < rc.top) + top = rc.top; + if (bottom < rc.bottom) + bottom = rc.bottom; + } + @property int width() { return right - left; } + @property int height() { return bottom - top; } + this(int x0, int y0, int x1, int y1) { + left = x0; + top = y0; + right = x1; + bottom = y1; + } + @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) + left = rc.left; + if (top < rc.top) + top = rc.top; + if (right > rc.right) + right = rc.right; + if (bottom > rc.bottom) + bottom = rc.bottom; + return right > left && bottom > top; + } + /// returns true if this rect has nonempty intersection with rc + bool intersects(Rect rc) { + if (rc.left >= right || rc.top >= bottom || rc.right <= left || rc.bottom <= top) + return false; + return true; + } + /// returns true if point is inside of this rectangle + bool isPointInside(Point pt) { + return pt.x >= left && pt.x < right && pt.y >= top && pt.y < bottom; + } + /// returns true if point is inside of this rectangle + bool isPointInside(int x, int y) { + return x >= left && x < right && y >= top && y < bottom; + } +} + +/// character glyph +align(1) struct Glyph { version (USE_OPENGL) { @@ -117,84 +117,84 @@ struct Glyph ///< 12: glyph data, arbitrary size ubyte[] glyph; } - -class RefCountedObject { - protected int _refCount; - @property int refCount() const { return _refCount; } - void addRef() { - _refCount++; - } - void releaseRef() { - if (--_refCount == 0) - destroy(this); - } - ~this() {} -} - -struct Ref(T) { // if (T is RefCountedObject) - private T _data; - alias get this; - @property bool isNull() const { return _data is null; } - @property int refCount() const { return _data !is null ? _data.refCount : 0; } - this(T data) { - _data = data; - if (_data !is null) - _data.addRef(); - } - this(this) { - if (_data !is null) - _data.addRef(); - } - ref Ref opAssign(ref Ref data) { - if (data._data == _data) - return this; - if (_data !is null) - _data.releaseRef(); - _data = data._data; - if (_data !is null) - _data.addRef(); - return this; - } - ref Ref opAssign(Ref data) { - if (data._data == _data) - return this; - if (_data !is null) - _data.releaseRef(); - _data = data._data; - if (_data !is null) - _data.addRef(); - return this; - } - ref Ref opAssign(T data) { - if (data == _data) - return this; - if (_data !is null) - _data.releaseRef(); - _data = data; - if (_data !is null) - _data.addRef(); - return this; - } - void clear() { - if (_data !is null) { - _data.releaseRef(); - _data = null; - } - } - @property T get() { - return _data; - } - @property const(T) get() const { - return _data; - } - ~this() { - if (_data !is null) - _data.releaseRef(); - } -} - - -// some utility functions + +class RefCountedObject { + protected int _refCount; + @property int refCount() const { return _refCount; } + void addRef() { + _refCount++; + } + void releaseRef() { + if (--_refCount == 0) + destroy(this); + } + ~this() {} +} + +struct Ref(T) { // if (T is RefCountedObject) + private T _data; + alias get this; + @property bool isNull() const { return _data is null; } + @property int refCount() const { return _data !is null ? _data.refCount : 0; } + this(T data) { + _data = data; + if (_data !is null) + _data.addRef(); + } + this(this) { + if (_data !is null) + _data.addRef(); + } + ref Ref opAssign(ref Ref data) { + if (data._data == _data) + return this; + if (_data !is null) + _data.releaseRef(); + _data = data._data; + if (_data !is null) + _data.addRef(); + return this; + } + ref Ref opAssign(Ref data) { + if (data._data == _data) + return this; + if (_data !is null) + _data.releaseRef(); + _data = data._data; + if (_data !is null) + _data.addRef(); + return this; + } + ref Ref opAssign(T data) { + if (data == _data) + return this; + if (_data !is null) + _data.releaseRef(); + _data = data; + if (_data !is null) + _data.addRef(); + return this; + } + void clear() { + if (_data !is null) { + _data.releaseRef(); + _data = null; + } + } + @property T get() { + return _data; + } + @property const(T) get() const { + return _data; + } + ~this() { + if (_data !is null) + _data.releaseRef(); + } +} + + +// some utility functions string fromStringz(const(char[]) s) { if (s is null) return null; diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index 1668aca6..19f7b9ed 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -220,7 +220,7 @@ class Window { private bool checkRemoveTracking(MouseEvent event) { import std.algorithm; bool res = false; - for(int i = _mouseTrackingWidgets.length - 1; i >=0; i--) { + for(int i = cast(int)_mouseTrackingWidgets.length - 1; i >=0; i--) { Widget w = _mouseTrackingWidgets[i]; if (!isChild(w)) { // std.algorithm.remove does not work for me diff --git a/src/dlangui/widgets/menu.d b/src/dlangui/widgets/menu.d index 45eb21d8..d3f7b4a3 100644 --- a/src/dlangui/widgets/menu.d +++ b/src/dlangui/widgets/menu.d @@ -1,204 +1,209 @@ -module dlangui.widgets.menu; - -import dlangui.core.events; -import dlangui.widgets.controls; -import dlangui.widgets.layouts; -import dlangui.widgets.lists; -import dlangui.widgets.popup; - -/// menu item properties -class MenuItem { - protected bool _checkable; - protected bool _checked; - protected bool _enabled; - protected Action _action; - protected MenuItem[] _subitems; - /// item action id, 0 if no action - @property int id() { return _action is null ? 0 : _action.id; } - /// returns count of submenu items - @property int subitemCount() { - return cast(int)_subitems.length; - } - /// returns submenu item by index - MenuItem subitem(int index) { - return _subitems[index]; - } - /// adds submenu item - MenuItem add(MenuItem subitem) { - _subitems ~= subitem; - return this; - } - /// adds submenu item from action - MenuItem add(Action subitemAction) { - _subitems ~= new MenuItem(subitemAction); - return this; - } - /// returns true if item is submenu (contains subitems) - @property bool isSubmenu() { - return _subitems.length > 0; - } - /// returns item label - @property UIString label() { - return _action.labelValue; - } - /// returns item action - @property const(Action) action() const { return _action; } - /// sets item action - @property MenuItem action(Action a) { _action = a; return this; } - this() { - _enabled = true; - } - this(Action action) { - _action = action; - _enabled = true; - } - ~this() { - // TODO - } -} - -/// widget to draw menu item -class MenuItemWidget : HorizontalLayout { - protected MenuItem _item; - protected TextWidget _label; - @property MenuItem item() { return _item; } - this(MenuItem item) { - id="menuitem"; - _item = item; - styleId = "MENU_ITEM"; - _label = new TextWidget("MENU_LABEL"); - _label.text = _item.label; - addChild(_label); - trackHover = true; - } -} - -/// base class for menus -class MenuWidgetBase : ListWidget { - protected MenuWidgetBase _parentMenu; - protected MenuItem _item; - protected PopupMenu _openedMenu; - protected PopupWidget _openedPopup; - protected bool delegate(MenuItem item) _onMenuItemClickListener; - /// menu item click listener - @property bool delegate(MenuItem item) onMenuItemListener() { return _onMenuItemClickListener; } - /// menu item click listener - @property MenuWidgetBase onMenuItemListener(bool delegate(MenuItem item) listener) { _onMenuItemClickListener = listener; return this; } - - this(MenuWidgetBase parentMenu, MenuItem item, Orientation orientation) { - _parentMenu = parentMenu; - _item = item; - this.orientation = orientation; - id = "popup_menu"; - styleId = "POPUP_MENU"; - WidgetListAdapter adapter = new WidgetListAdapter(); - for (int i=0; i < _item.subitemCount; i++) { - MenuItem subitem = _item.subitem(i); - MenuItemWidget widget = new MenuItemWidget(subitem); - if (orientation == Orientation.Horizontal) - widget.styleId = "MAIN_MENU_ITEM"; - adapter.widgets.add(widget); - } - ownAdapter = adapter; - } - - protected void onPopupClosed(PopupWidget p) { - _openedPopup = null; - _openedMenu = null; - selectItem(-1); - } - - protected void openSubmenu(MenuItemWidget itemWidget) { - if (_openedPopup !is null) { - _openedPopup.close(); - } - PopupMenu popupMenu = new PopupMenu(itemWidget.item, this); - PopupWidget popup = window.showPopup(popupMenu, itemWidget, orientation == Orientation.Horizontal ? PopupAlign.Below : PopupAlign.Right); - popup.onPopupCloseListener = &onPopupClosed; - popup.flags = PopupFlags.CloseOnClickOutside; - _openedPopup = popup; - _openedMenu = popupMenu; - } - +module dlangui.widgets.menu; + +import dlangui.core.events; +import dlangui.widgets.controls; +import dlangui.widgets.layouts; +import dlangui.widgets.lists; +import dlangui.widgets.popup; + +/// menu item properties +class MenuItem { + protected bool _checkable; + protected bool _checked; + protected bool _enabled; + protected Action _action; + protected MenuItem[] _subitems; + /// item action id, 0 if no action + @property int id() { return _action is null ? 0 : _action.id; } + /// returns count of submenu items + @property int subitemCount() { + return cast(int)_subitems.length; + } + /// returns submenu item by index + MenuItem subitem(int index) { + return _subitems[index]; + } + /// adds submenu item + MenuItem add(MenuItem subitem) { + _subitems ~= subitem; + return this; + } + /// adds submenu item from action + MenuItem add(Action subitemAction) { + _subitems ~= new MenuItem(subitemAction); + return this; + } + /// returns true if item is submenu (contains subitems) + @property bool isSubmenu() { + return _subitems.length > 0; + } + /// returns item label + @property UIString label() { + return _action.labelValue; + } + /// returns item action + @property const(Action) action() const { return _action; } + /// sets item action + @property MenuItem action(Action a) { _action = a; return this; } + this() { + _enabled = true; + } + this(Action action) { + _action = action; + _enabled = true; + } + ~this() { + // TODO + } +} + +/// widget to draw menu item +class MenuItemWidget : HorizontalLayout { + protected MenuItem _item; + protected TextWidget _label; + @property MenuItem item() { return _item; } + this(MenuItem item) { + id="menuitem"; + _item = item; + styleId = "MENU_ITEM"; + _label = new TextWidget("MENU_LABEL"); + _label.text = _item.label; + addChild(_label); + trackHover = true; + } +} + +/// base class for menus +class MenuWidgetBase : ListWidget { + protected MenuWidgetBase _parentMenu; + protected MenuItem _item; + protected PopupMenu _openedMenu; + protected PopupWidget _openedPopup; + protected bool delegate(MenuItem item) _onMenuItemClickListener; + /// menu item click listener + @property bool delegate(MenuItem item) onMenuItemListener() { return _onMenuItemClickListener; } + /// menu item click listener + @property MenuWidgetBase onMenuItemListener(bool delegate(MenuItem item) listener) { _onMenuItemClickListener = listener; return this; } + + this(MenuWidgetBase parentMenu, MenuItem item, Orientation orientation) { + _parentMenu = parentMenu; + _item = item; + this.orientation = orientation; + id = "popup_menu"; + styleId = "POPUP_MENU"; + WidgetListAdapter adapter = new WidgetListAdapter(); + for (int i=0; i < _item.subitemCount; i++) { + MenuItem subitem = _item.subitem(i); + MenuItemWidget widget = new MenuItemWidget(subitem); + if (orientation == Orientation.Horizontal) + widget.styleId = "MAIN_MENU_ITEM"; + adapter.widgets.add(widget); + } + ownAdapter = adapter; + } + + protected void onPopupClosed(PopupWidget p) { + _openedPopup = null; + _openedMenu = null; + selectItem(-1); + } + + protected void openSubmenu(MenuItemWidget itemWidget) { + if (_openedPopup !is null) { + _openedPopup.close(); + } + PopupMenu popupMenu = new PopupMenu(itemWidget.item, this); + PopupWidget popup = window.showPopup(popupMenu, itemWidget, orientation == Orientation.Horizontal ? PopupAlign.Below : PopupAlign.Right); + popup.onPopupCloseListener = &onPopupClosed; + popup.flags = PopupFlags.CloseOnClickOutside; + _openedPopup = popup; + _openedMenu = popupMenu; + } + /// override to handle change of selection override protected void selectionChanged(int index, int previouslySelectedItem = -1) { MenuItemWidget itemWidget = index >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(index) : null; + MenuItemWidget prevWidget = previouslySelectedItem >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(previouslySelectedItem) : null; + if (prevWidget !is null) { + if (_openedPopup !is null) + _openedPopup.close(); + } if (itemWidget !is null) { - if (itemWidget.item.isSubmenu()) { - if (_selectOnHover) { - openSubmenu(itemWidget); - } - } else { - // normal item - } - } + if (itemWidget.item.isSubmenu()) { + if (_selectOnHover) { + openSubmenu(itemWidget); + } + } else { + // normal item + } + } } - - protected void onMenuItem(MenuItem item) { - if (_openedPopup !is null) { - _openedPopup.close(); - _openedPopup = null; - } - if (_parentMenu !is null) - _parentMenu.onMenuItem(item); - else { - // top level handling - Log.d("onMenuItem ", item.id); - selectItem(-1); - selectOnHover = false; - bool delegate(MenuItem item) listener = _onMenuItemClickListener; - PopupWidget popup = cast(PopupWidget)parent; - if (popup) - popup.close(); - // this pointer now can be invalid - if popup removed - if (listener !is null) - listener(item); - } - } - + + protected void onMenuItem(MenuItem item) { + if (_openedPopup !is null) { + _openedPopup.close(); + _openedPopup = null; + } + if (_parentMenu !is null) + _parentMenu.onMenuItem(item); + else { + // top level handling + Log.d("onMenuItem ", item.id); + selectItem(-1); + selectOnHover = false; + bool delegate(MenuItem item) listener = _onMenuItemClickListener; + PopupWidget popup = cast(PopupWidget)parent; + if (popup) + popup.close(); + // this pointer now can be invalid - if popup removed + if (listener !is null) + listener(item); + } + } + /// override to handle mouse up on item override protected void itemClicked(int index) { MenuItemWidget itemWidget = index >= 0 ? cast(MenuItemWidget)_adapter.itemWidget(index) : null; if (itemWidget !is null) { Log.d("Menu Item clicked ", itemWidget.item.action.id); - if (itemWidget.item.isSubmenu()) { - // submenu clicked - if (_clickOnButtonDown && _openedPopup !is null && _openedMenu._item is itemWidget.item) { - // second click on main menu opened item - _openedPopup.close(); - _openedPopup = null; - selectItem(-1); - selectOnHover = false; - } else { - openSubmenu(itemWidget); - selectOnHover = true; - } + if (itemWidget.item.isSubmenu()) { + // submenu clicked + if (_clickOnButtonDown && _openedPopup !is null && _openedMenu._item is itemWidget.item) { + // second click on main menu opened item + _openedPopup.close(); + _openedPopup = null; + selectItem(-1); + selectOnHover = false; + } else { + openSubmenu(itemWidget); + selectOnHover = true; + } } else { // normal item onMenuItem(itemWidget.item); } } } -} - -/// main menu (horizontal) -class MainMenu : MenuWidgetBase { - - this(MenuItem item) { - super(null, item, Orientation.Horizontal); - id = "MAIN_MENU"; - styleId = "MAIN_MENU"; - _clickOnButtonDown = true; - } -} - -/// popup menu widget (vertical layout of items) -class PopupMenu : MenuWidgetBase { - - this(MenuItem item, MenuWidgetBase parentMenu = null) { - super(parentMenu, item, Orientation.Vertical); - id = "POPUP_MENU"; - styleId = "POPUP_MENU"; - selectOnHover = true; - } -} +} + +/// main menu (horizontal) +class MainMenu : MenuWidgetBase { + + this(MenuItem item) { + super(null, item, Orientation.Horizontal); + id = "MAIN_MENU"; + styleId = "MAIN_MENU"; + _clickOnButtonDown = true; + } +} + +/// popup menu widget (vertical layout of items) +class PopupMenu : MenuWidgetBase { + + this(MenuItem item, MenuWidgetBase parentMenu = null) { + super(parentMenu, item, Orientation.Vertical); + id = "POPUP_MENU"; + styleId = "POPUP_MENU"; + selectOnHover = true; + } +}