mirror of https://github.com/buggins/dlangui.git
popup menus; popup menu for editors
This commit is contained in:
parent
0b35db01c7
commit
6d518ed5c0
|
@ -93,7 +93,7 @@ extern (C) int UIAppMain(string[] args) {
|
||||||
mainMenuItems.add(windowItem);
|
mainMenuItems.add(windowItem);
|
||||||
mainMenuItems.add(helpItem);
|
mainMenuItems.add(helpItem);
|
||||||
MainMenu mainMenu = new MainMenu(mainMenuItems);
|
MainMenu mainMenu = new MainMenu(mainMenuItems);
|
||||||
mainMenu.onMenuItemListener = delegate(MenuItem item) {
|
mainMenu.onMenuItemClickListener = delegate(MenuItem item) {
|
||||||
Log.d("mainMenu.onMenuItemListener", item.label);
|
Log.d("mainMenu.onMenuItemListener", item.label);
|
||||||
const Action a = item.action;
|
const Action a = item.action;
|
||||||
if (a) {
|
if (a) {
|
||||||
|
|
|
@ -97,6 +97,11 @@ struct Collection(T, bool ownItems = false) {
|
||||||
_items[index] = item;
|
_items[index] = item;
|
||||||
_len++;
|
_len++;
|
||||||
}
|
}
|
||||||
|
/// add all items from other collection
|
||||||
|
void addAll(ref Collection!(T, ownItems) v) {
|
||||||
|
for (int i = 0; i < v.length; i++)
|
||||||
|
add(v[i]);
|
||||||
|
}
|
||||||
/// support for appending (~=, +=) and removing by value (-=)
|
/// support for appending (~=, +=) and removing by value (-=)
|
||||||
ref Collection opOpAssign(string op)(T item) {
|
ref Collection opOpAssign(string op)(T item) {
|
||||||
static if (op.equal("~") || op.equal("+")) {
|
static if (op.equal("~") || op.equal("+")) {
|
||||||
|
|
|
@ -170,6 +170,11 @@ struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length ==
|
||||||
alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
|
alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0]));
|
||||||
alias slot_t = return_t delegate(params_t);
|
alias slot_t = return_t delegate(params_t);
|
||||||
private Collection!slot_t _listeners;
|
private Collection!slot_t _listeners;
|
||||||
|
|
||||||
|
this(ref Signal!T1 v) {
|
||||||
|
_listeners.addAll(v._listeners);
|
||||||
|
}
|
||||||
|
|
||||||
/// returns true if listener is assigned
|
/// returns true if listener is assigned
|
||||||
final bool assigned() {
|
final bool assigned() {
|
||||||
return _listeners.length > 0;
|
return _listeners.length > 0;
|
||||||
|
|
|
@ -805,7 +805,7 @@ class EditableContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// base for all editor widgets
|
/// base for all editor widgets
|
||||||
class EditWidgetBase : WidgetGroup, EditableContentListener {
|
class EditWidgetBase : WidgetGroup, EditableContentListener, MenuItemActionHandler {
|
||||||
protected EditableContent _content;
|
protected EditableContent _content;
|
||||||
protected Rect _clientRc;
|
protected Rect _clientRc;
|
||||||
|
|
||||||
|
@ -905,6 +905,12 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
|
||||||
_popupMenu = popupMenu;
|
_popupMenu = popupMenu;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
override bool onMenuItemAction(const Action action) {
|
||||||
|
return handleAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
/// returns true if widget can show popup (e.g. by mouse right click at point x,y)
|
/// returns true if widget can show popup (e.g. by mouse right click at point x,y)
|
||||||
override bool canShowPopupMenu(int x, int y) {
|
override bool canShowPopupMenu(int x, int y) {
|
||||||
if (_popupMenu is null)
|
if (_popupMenu is null)
|
||||||
|
@ -914,13 +920,38 @@ class EditWidgetBase : WidgetGroup, EditableContentListener {
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
/// override to change popup menu items state
|
||||||
|
override bool isActionEnabled(const Action action) {
|
||||||
|
switch (action.id) {
|
||||||
|
case EditorActions.Copy:
|
||||||
|
case EditorActions.Cut:
|
||||||
|
return !_selectionRange.empty;
|
||||||
|
case EditorActions.Paste:
|
||||||
|
return Platform.instance.getClipboardText().length > 0;
|
||||||
|
case EditorActions.Undo:
|
||||||
|
return _content.hasUndo;
|
||||||
|
case EditorActions.Redo:
|
||||||
|
return _content.hasRedo;
|
||||||
|
default:
|
||||||
|
return super.isActionEnabled(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
/// shows popup at (x,y)
|
/// shows popup at (x,y)
|
||||||
override void showPopupMenu(int x, int y) {
|
override void showPopupMenu(int x, int y) {
|
||||||
/// if preparation signal handler assigned, call it; don't show popup if false is returned from handler
|
/// if preparation signal handler assigned, call it; don't show popup if false is returned from handler
|
||||||
if (_popupMenu.onBeforeOpeningSubmenu.assigned)
|
if (_popupMenu.onBeforeOpeningSubmenu.assigned)
|
||||||
if (!_popupMenu.onBeforeOpeningSubmenu(_popupMenu))
|
if (!_popupMenu.onBeforeOpeningSubmenu(_popupMenu))
|
||||||
return;
|
return;
|
||||||
|
for (int i = 0; i < _popupMenu.subitemCount; i++) {
|
||||||
|
MenuItem item = _popupMenu.subitem(i);
|
||||||
|
if (item.action && isActionEnabled(item.action)) {
|
||||||
|
item.enabled = true;
|
||||||
|
} else {
|
||||||
|
item.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
PopupMenu popupMenu = new PopupMenu(_popupMenu);
|
PopupMenu popupMenu = new PopupMenu(_popupMenu);
|
||||||
|
popupMenu.onMenuItemActionListener = this;
|
||||||
PopupWidget popup = window.showPopup(popupMenu, this, PopupAlign.Point | PopupAlign.Right, x, y);
|
PopupWidget popup = window.showPopup(popupMenu, this, PopupAlign.Point | PopupAlign.Right, x, y);
|
||||||
popup.flags = PopupFlags.CloseOnClickOutside;
|
popup.flags = PopupFlags.CloseOnClickOutside;
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,14 @@ class MenuItem {
|
||||||
/// sets item action
|
/// sets item action
|
||||||
@property MenuItem action(Action a) { _action = a; return this; }
|
@property MenuItem action(Action a) { _action = a; return this; }
|
||||||
|
|
||||||
|
/// menu item Enabled flag
|
||||||
|
@property bool enabled() { return _enabled; }
|
||||||
|
/// menu item Enabled flag
|
||||||
|
@property MenuItem enabled(bool enabled) {
|
||||||
|
_enabled = enabled;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/// handle menu item click
|
/// handle menu item click
|
||||||
Signal!(void, MenuItem) onMenuItem;
|
Signal!(void, MenuItem) onMenuItem;
|
||||||
/// prepare for opening of submenu, return true if opening is allowed
|
/// prepare for opening of submenu, return true if opening is allowed
|
||||||
|
@ -218,16 +226,20 @@ class MenuItemWidget : WidgetGroup {
|
||||||
_mainMenu = mainMenu;
|
_mainMenu = mainMenu;
|
||||||
_item = item;
|
_item = item;
|
||||||
styleId = "MENU_ITEM";
|
styleId = "MENU_ITEM";
|
||||||
|
if (!item.enabled)
|
||||||
|
resetState(State.Enabled);
|
||||||
// icon
|
// icon
|
||||||
if (_item.action && _item.action.iconId.length) {
|
if (_item.action && _item.action.iconId.length) {
|
||||||
_icon = new ImageWidget("MENU_ICON", _item.action.iconId);
|
_icon = new ImageWidget("MENU_ICON", _item.action.iconId);
|
||||||
_icon.styleId = "MENU_ICON";
|
_icon.styleId = "MENU_ICON";
|
||||||
|
_icon.state = State.Parent;
|
||||||
addChild(_icon);
|
addChild(_icon);
|
||||||
}
|
}
|
||||||
// label
|
// label
|
||||||
_label = new TextWidget("MENU_LABEL");
|
_label = new TextWidget("MENU_LABEL");
|
||||||
_label.text = _item.label;
|
_label.text = _item.label;
|
||||||
_label.styleId = _mainMenu ? "MAIN_MENU_LABEL" : "MENU_LABEL";
|
_label.styleId = _mainMenu ? "MAIN_MENU_LABEL" : "MENU_LABEL";
|
||||||
|
_label.state = State.Parent;
|
||||||
addChild(_label);
|
addChild(_label);
|
||||||
// accelerator
|
// accelerator
|
||||||
dstring acc = _item.acceleratorText;
|
dstring acc = _item.acceleratorText;
|
||||||
|
@ -235,6 +247,7 @@ class MenuItemWidget : WidgetGroup {
|
||||||
_accel = new TextWidget("MENU_ACCEL");
|
_accel = new TextWidget("MENU_ACCEL");
|
||||||
_accel.styleId = "MENU_ACCEL";
|
_accel.styleId = "MENU_ACCEL";
|
||||||
_accel.text = acc;
|
_accel.text = acc;
|
||||||
|
_accel.state = State.Parent;
|
||||||
addChild(_accel);
|
addChild(_accel);
|
||||||
}
|
}
|
||||||
trackHover = true;
|
trackHover = true;
|
||||||
|
@ -242,6 +255,14 @@ class MenuItemWidget : WidgetGroup {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface MenuItemClickHandler {
|
||||||
|
bool onMenuItemClick(MenuItem item);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MenuItemActionHandler {
|
||||||
|
bool onMenuItemAction(const Action action);
|
||||||
|
}
|
||||||
|
|
||||||
/// base class for menus
|
/// base class for menus
|
||||||
class MenuWidgetBase : ListWidget {
|
class MenuWidgetBase : ListWidget {
|
||||||
protected MenuWidgetBase _parentMenu;
|
protected MenuWidgetBase _parentMenu;
|
||||||
|
@ -249,11 +270,11 @@ class MenuWidgetBase : ListWidget {
|
||||||
protected PopupMenu _openedMenu;
|
protected PopupMenu _openedMenu;
|
||||||
protected PopupWidget _openedPopup;
|
protected PopupWidget _openedPopup;
|
||||||
protected int _openedPopupIndex;
|
protected int _openedPopupIndex;
|
||||||
protected bool delegate(MenuItem item) _onMenuItemClickListener;
|
|
||||||
/// menu item click listener
|
/// menu item click listener
|
||||||
@property bool delegate(MenuItem item) onMenuItemListener() { return _onMenuItemClickListener; }
|
Signal!MenuItemClickHandler onMenuItemClickListener;
|
||||||
/// menu item click listener
|
/// menu item action listener
|
||||||
@property MenuWidgetBase onMenuItemListener(bool delegate(MenuItem item) listener) { _onMenuItemClickListener = listener; return this; }
|
Signal!MenuItemActionHandler onMenuItemActionListener;
|
||||||
|
|
||||||
this(MenuWidgetBase parentMenu, MenuItem item, Orientation orientation) {
|
this(MenuWidgetBase parentMenu, MenuItem item, Orientation orientation) {
|
||||||
_parentMenu = parentMenu;
|
_parentMenu = parentMenu;
|
||||||
|
@ -392,13 +413,22 @@ class MenuWidgetBase : ListWidget {
|
||||||
selectItem(-1);
|
selectItem(-1);
|
||||||
setHoverItem(-1);
|
setHoverItem(-1);
|
||||||
selectOnHover = false;
|
selectOnHover = false;
|
||||||
bool delegate(MenuItem item) listener = _onMenuItemClickListener;
|
|
||||||
|
// copy menu item click listeners
|
||||||
|
Signal!MenuItemClickHandler onMenuItemClickListenerCopy = onMenuItemClickListener;
|
||||||
|
// copy item action listeners
|
||||||
|
Signal!MenuItemActionHandler onMenuItemActionListenerCopy = onMenuItemActionListener;
|
||||||
|
|
||||||
PopupWidget popup = cast(PopupWidget)parent;
|
PopupWidget popup = cast(PopupWidget)parent;
|
||||||
if (popup)
|
if (popup)
|
||||||
popup.close();
|
popup.close();
|
||||||
// this pointer now can be invalid - if popup removed
|
// this pointer now can be invalid - if popup removed
|
||||||
if (listener !is null)
|
if (onMenuItemClickListenerCopy.assigned)
|
||||||
listener(item);
|
if (onMenuItemClickListenerCopy(item))
|
||||||
|
return;
|
||||||
|
// this pointer now can be invalid - if popup removed
|
||||||
|
if (onMenuItemActionListenerCopy.assigned)
|
||||||
|
onMenuItemActionListenerCopy(item.action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -526,10 +556,21 @@ class MainMenu : MenuWidgetBase {
|
||||||
|
|
||||||
override protected void onMenuItem(MenuItem item) {
|
override protected void onMenuItem(MenuItem item) {
|
||||||
debug Log.d("MainMenu.onMenuItem ", item.action.label);
|
debug Log.d("MainMenu.onMenuItem ", item.action.label);
|
||||||
bool delegate(MenuItem item) listener = _onMenuItemClickListener;
|
|
||||||
|
// copy menu item click listeners
|
||||||
|
Signal!MenuItemClickHandler onMenuItemClickListenerCopy = onMenuItemClickListener;
|
||||||
|
// copy item action listeners
|
||||||
|
Signal!MenuItemActionHandler onMenuItemActionListenerCopy = onMenuItemActionListener;
|
||||||
|
|
||||||
deactivate();
|
deactivate();
|
||||||
if (listener !is null)
|
|
||||||
listener(item);
|
// this pointer now can be invalid - if popup removed
|
||||||
|
if (onMenuItemClickListenerCopy.assigned)
|
||||||
|
if (onMenuItemClickListenerCopy(item))
|
||||||
|
return;
|
||||||
|
// this pointer now can be invalid - if popup removed
|
||||||
|
if (onMenuItemActionListenerCopy.assigned)
|
||||||
|
onMenuItemActionListenerCopy(item.action);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// return true if main menu is activated (focused or has open submenu)
|
/// return true if main menu is activated (focused or has open submenu)
|
||||||
|
|
|
@ -757,9 +757,9 @@ Theme createDefaultTheme() {
|
||||||
menuItem.createState(State.Selected, State.Selected).backgroundColor(0x00F8F9Fa);
|
menuItem.createState(State.Selected, State.Selected).backgroundColor(0x00F8F9Fa);
|
||||||
menuItem.createState(State.Hovered, State.Hovered).backgroundColor(0xC0FFFF00);
|
menuItem.createState(State.Hovered, State.Hovered).backgroundColor(0xC0FFFF00);
|
||||||
res.createSubstyle("MENU_ICON").setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left);
|
res.createSubstyle("MENU_ICON").setMargins(2,2,2,2).alignment(Align.VCenter|Align.Left);
|
||||||
res.createSubstyle("MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TextFlag.UnderlineHotKeys);
|
res.createSubstyle("MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TextFlag.UnderlineHotKeys).createState(State.Enabled,0).textColor(0x80404040);
|
||||||
res.createSubstyle("MAIN_MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TEXT_FLAGS_USE_PARENT);
|
res.createSubstyle("MAIN_MENU_LABEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).textFlags(TEXT_FLAGS_USE_PARENT).createState(State.Enabled,0).textColor(0x80404040);
|
||||||
res.createSubstyle("MENU_ACCEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left);
|
res.createSubstyle("MENU_ACCEL").setMargins(4,2,4,2).alignment(Align.VCenter|Align.Left).createState(State.Enabled,0).textColor(0x80404040);
|
||||||
|
|
||||||
Style transparentButtonBackground = res.createSubstyle("TRANSPARENT_BUTTON_BACKGROUND").backgroundImageId("transparent_button_background").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ;
|
Style transparentButtonBackground = res.createSubstyle("TRANSPARENT_BUTTON_BACKGROUND").backgroundImageId("transparent_button_background").setPadding(4,2,4,2); //.backgroundColor(0xE0E080) ;
|
||||||
//transparentButtonBackground.createState(State.Focused, State.Focused).backgroundColor(0xC0C0C000);
|
//transparentButtonBackground.createState(State.Focused, State.Focused).backgroundColor(0xC0C0C000);
|
||||||
|
|
|
@ -1050,6 +1050,10 @@ class Widget {
|
||||||
void showPopupMenu(int x, int y) {
|
void showPopupMenu(int x, int y) {
|
||||||
// override to show popup
|
// override to show popup
|
||||||
}
|
}
|
||||||
|
/// override to change popup menu items state
|
||||||
|
bool isActionEnabled(const Action action) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// ===========================================================
|
// ===========================================================
|
||||||
// Widget hierarhy methods
|
// Widget hierarhy methods
|
||||||
|
|
Loading…
Reference in New Issue