radio button and checkbox menu item types support; i18n of example1

This commit is contained in:
Vadim Lopatin 2014-05-19 10:29:15 +04:00
parent 2da47b106a
commit 908fbd396e
3 changed files with 172 additions and 32 deletions
examples/example1
res/i18n
src
src/dlangui/widgets

View File

@ -1 +1,26 @@
EXIT=Exit
EXIT=Exit
MENU_FILE=&File
MENU_FILE_OPEN=&Open
MENU_FILE_OPEN_RECENT=Open recent
MENU_FILE_SAVE=&Save
MENU_FILE_EXIT=E&xit
MENU_EDIT=&Edit
MENU_EDIT_COPY=&Copy
MENU_EDIT_PASTE=&Paste
MENU_EDIT_CUT=Cu&t
MENU_EDIT_UNDO=&Undo
MENU_EDIT_REDO=&Redo
MENU_EDIT_PREFERENCES=&Preferences
MENU_VIEW=&View
MENU_VIEW_LANGUAGE=Interface &Language
MENU_VIEW_LANGUAGE_EN=English
MENU_VIEW_LANGUAGE_RU=Russian
MENU_VIEW_THEME=&Theme
MENU_VIEW_THEME_DEFAULT=&Default
MENU_VIEW_THEME_CUSTOM1=&Custom 1
MENU_WINDOW=&Window
MENU_WINDOW_PREFERENCES=&Preferences
MENU_HELP=&Help
MENU_HELP_VIEW_HELP=&View help
MENU_HELP_ABOUT=&About

View File

@ -102,40 +102,52 @@ extern (C) int UIAppMain(string[] args) {
static if (true) {
VerticalLayout contentLayout = new VerticalLayout();
MenuItem mainMenuItems = new MenuItem();
MenuItem fileItem = new MenuItem(new Action(1, "&File"d));
fileItem.add(new Action(10, "&Open..."d, "document-open", KeyCode.KEY_O, KeyFlag.Control));
fileItem.add(new Action(11, "&Save..."d, "document-save", KeyCode.KEY_S, KeyFlag.Control));
MenuItem openRecentItem = new MenuItem(new Action(13, "Open recent..."d, "document-open-recent"));
MenuItem fileItem = new MenuItem(new Action(1, "MENU_FILE"));
fileItem.add(new Action(10, "MENU_FILE_OPEN"c, "document-open", KeyCode.KEY_O, KeyFlag.Control));
fileItem.add(new Action(11, "MENU_FILE_SAVE"c, "document-save", KeyCode.KEY_S, KeyFlag.Control));
MenuItem openRecentItem = new MenuItem(new Action(13, "MENU_FILE_OPEN_RECENT", "document-open-recent"));
openRecentItem.add(new Action(100, "&1: File 1"d));
openRecentItem.add(new Action(101, "&2: File 2"d));
openRecentItem.add(new Action(102, "&3: File 3"d));
openRecentItem.add(new Action(103, "&4: File 4"d));
openRecentItem.add(new Action(104, "&5: File 5"d));
fileItem.add(openRecentItem);
fileItem.add(new Action(12, "E&xit"d, "document-close", KeyCode.KEY_X, KeyFlag.Alt));
MenuItem editItem = new MenuItem(new Action(2, "&Edit"d));
editItem.add(new Action(EditorActions.Copy, "Copy"d, "edit-copy", KeyCode.KEY_C, KeyFlag.Control));
editItem.add(new Action(EditorActions.Paste, "Paste"d, "edit-paste", KeyCode.KEY_V, KeyFlag.Control));
editItem.add(new Action(EditorActions.Cut, "Cut"d, "edit-cut", KeyCode.KEY_X, KeyFlag.Control));
editItem.add(new Action(EditorActions.Undo, "Undo"d, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control));
editItem.add(new Action(EditorActions.Redo, "Redo"d, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control));
editItem.add(new Action(20, "Preferences..."d));
fileItem.add(new Action(12, "MENU_FILE_EXIT"c, "document-close"c, KeyCode.KEY_X, KeyFlag.Alt));
MenuItem editItem = new MenuItem(new Action(2, "MENU_EDIT"));
editItem.add(new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy", KeyCode.KEY_C, KeyFlag.Control));
editItem.add(new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste", KeyCode.KEY_V, KeyFlag.Control));
editItem.add(new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut", KeyCode.KEY_X, KeyFlag.Control));
editItem.add(new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control));
editItem.add(new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control));
editItem.add(new Action(20, "MENU_EDIT_PREFERENCES"));
MenuItem editPopupItem = new MenuItem(null);
editPopupItem.add(new Action(EditorActions.Copy, "Copy"d, "edit-copy", KeyCode.KEY_C, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Paste, "Paste"d, "edit-paste", KeyCode.KEY_V, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Cut, "Cut"d, "edit-cut", KeyCode.KEY_X, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Undo, "Undo"d, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Redo, "Redo"d, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Copy, "MENU_EDIT_COPY"c, "edit-copy", KeyCode.KEY_C, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Paste, "MENU_EDIT_PASTE"c, "edit-paste", KeyCode.KEY_V, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Cut, "MENU_EDIT_CUT"c, "edit-cut", KeyCode.KEY_X, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Undo, "MENU_EDIT_UNDO"c, "edit-undo", KeyCode.KEY_Z, KeyFlag.Control));
editPopupItem.add(new Action(EditorActions.Redo, "MENU_EDIT_REDO"c, "edit-redo", KeyCode.KEY_Y, KeyFlag.Control));
MenuItem windowItem = new MenuItem(new Action(3, "&Window"d));
windowItem.add(new Action(30, "Preferences"d));
MenuItem helpItem = new MenuItem(new Action(4, "Help"d));
helpItem.add(new Action(40, "View Help"d));
helpItem.add(new Action(41, "About"d));
MenuItem viewItem = new MenuItem(new Action(60, "MENU_VIEW"));
MenuItem langItem = new MenuItem(new Action(61, "MENU_VIEW_LANGUAGE"));
langItem.add((new MenuItem(new Action(611, "MENU_VIEW_LANGUAGE_EN"))).type(MenuItemType.Radio).checked(true));
langItem.add((new MenuItem(new Action(612, "MENU_VIEW_LANGUAGE_RU"))).type(MenuItemType.Radio));
MenuItem themeItem = new MenuItem(new Action(62, "MENU_VIEW_THEME"));
themeItem.add((new MenuItem(new Action(621, "MENU_VIEW_THEME_DEFAULT"))).type(MenuItemType.Radio).checked(true));
themeItem.add((new MenuItem(new Action(622, "MENU_VIEW_THEME_CUSTOM1"))).type(MenuItemType.Radio));
viewItem.add(langItem);
viewItem.add(themeItem);
MenuItem windowItem = new MenuItem(new Action(3, "MENU_WINDOW"));
windowItem.add(new Action(30, "MENU_WINDOW_PREFERENCES"));
MenuItem helpItem = new MenuItem(new Action(4, "MENU_HELP"));
helpItem.add(new Action(40, "MENU_HELP_VIEW_HELP"));
helpItem.add(new Action(41, "MENU_HELP_ABOUT"));
mainMenuItems.add(fileItem);
mainMenuItems.add(editItem);
mainMenuItems.add(windowItem);
mainMenuItems.add(viewItem);
mainMenuItems.add(windowItem);
mainMenuItems.add(helpItem);
MainMenu mainMenu = new MainMenu(mainMenuItems);
mainMenu.onMenuItemClickListener = delegate(MenuItem item) {

View File

@ -28,24 +28,91 @@ import dlangui.widgets.layouts;
import dlangui.widgets.lists;
import dlangui.widgets.popup;
/// menu item type
enum MenuItemType {
/// normal menu item
Normal,
/// menu item - checkbox
Check,
/// menu item - radio button
Radio,
/// menu separator (horizontal line)
Separator,
/// submenu - contains child items
Submenu
}
/// menu item properties
class MenuItem {
protected bool _checkable;
protected bool _checked;
protected bool _enabled;
protected MenuItemType _type = MenuItemType.Normal;
protected Action _action;
protected MenuItem[] _subitems;
protected MenuItem _parent;
/// 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 subitem index for item, -1 if item is not direct subitem of this
@property int subitemIndex(MenuItem item) {
for (int i = 0; i < _subitems.length; i++)
if (_subitems[i] is item)
return i;
return -1;
}
/// returns submenu item by index
MenuItem subitem(int index) {
return _subitems[index];
}
@property MenuItemType type() const {
if (_subitems.length > 0) // if there are children, force type to Submenu
return MenuItemType.Submenu;
return _type;
}
/// set new MenuItemType
@property MenuItem type(MenuItemType type) {
_type = type;
return this;
}
/// get check for checkbox or radio button item
@property bool checked() {
return _checked;
}
/// check radio button with specified index, uncheck other radio buttons in group (group consists of sequence of radio button items; other item type - end of group)
protected void checkRadioButton(int index) {
// find bounds of group
int start = index;
int end = index;
for (; start > 0 && _subitems[start - 1].type == MenuItemType.Radio; start--) {
// do nothing
}
for (; end < _subitems.length - 1 && _subitems[end + 1].type == MenuItemType.Radio; end++) {
// do nothing
}
// check item with specified index, uncheck others
for (int i = start; i <= end; i++)
_subitems[i]._checked = (i == index);
}
/// set check for checkbox or radio button item
@property MenuItem checked(bool flg) {
if (_checked == flg)
return this;
_checked = flg;
if (flg && _parent && type == MenuItemType.Radio) {
int index = _parent.subitemIndex(this);
if (index >= 0) {
_parent.checkRadioButton(index);
}
}
return this;
}
/// get hotkey character from label (e.g. 'F' for item labeled "&File"), 0 if no hotkey
dchar getHotkey() {
dstring s = label;
@ -74,12 +141,12 @@ class MenuItem {
/// adds submenu item
MenuItem add(MenuItem subitem) {
_subitems ~= subitem;
subitem._parent = this;
return this;
}
/// adds submenu item from action
MenuItem add(Action subitemAction) {
_subitems ~= new MenuItem(subitemAction);
return this;
return add(new MenuItem(subitemAction));
}
/// returns text description for first accelerator of action; null if no accelerators
@property dstring acceleratorText() {
@ -101,7 +168,7 @@ class MenuItem {
@property MenuItem action(Action a) { _action = a; return this; }
/// menu item Enabled flag
@property bool enabled() { return _enabled; }
@property bool enabled() { return _enabled && type != MenuItemType.Separator; }
/// menu item Enabled flag
@property MenuItem enabled(bool enabled) {
_enabled = enabled;
@ -166,6 +233,7 @@ class MenuItemWidget : WidgetGroup {
}
/// Measure widget according to desired width and height constraints. (Step 1 of two phase layout).
override void measure(int parentWidth, int parentHeight) {
updateState();
Rect m = margins;
Rect p = padding;
// calc size constraints for children
@ -206,6 +274,17 @@ class MenuItemWidget : WidgetGroup {
_label.layout(labelRc);
}
protected void updateState() {
if (_item.enabled)
setState(State.Enabled);
else
resetState(State.Enabled);
if (_item.checked)
setState(State.Checked);
else
resetState(State.Checked);
}
/// Draw widget at its position to buffer
override void onDraw(DrawBuf buf) {
if (visibility != Visibility.Visible)
@ -214,6 +293,7 @@ class MenuItemWidget : WidgetGroup {
Rect rc = _pos;
applyMargins(rc);
applyPadding(rc);
updateState();
auto saver = ClipRectSaver(buf, rc, alpha);
for (int i = 0; i < _children.count; i++) {
Widget item = _children.get(i);
@ -228,11 +308,15 @@ class MenuItemWidget : WidgetGroup {
_mainMenu = mainMenu;
_item = item;
styleId = "MENU_ITEM";
if (!item.enabled)
resetState(State.Enabled);
updateState();
string iconId = _item.action.iconId;
if (_item.type == MenuItemType.Check)
iconId = "btn_check";
else if (_item.type == MenuItemType.Radio)
iconId = "btn_radio";
// icon
if (_item.action && _item.action.iconId.length) {
_icon = new ImageWidget("MENU_ICON", _item.action.iconId);
if (_item.action && iconId.length) {
_icon = new ImageWidget("MENU_ICON", iconId);
_icon.styleId = "MENU_ICON";
_icon.state = State.Parent;
addChild(_icon);
@ -245,11 +329,15 @@ class MenuItemWidget : WidgetGroup {
addChild(_label);
// accelerator
dstring acc = _item.acceleratorText;
if (_item.isSubmenu && !mainMenu)
acc = "‣"d;
if (acc !is null) {
_accel = new TextWidget("MENU_ACCEL");
_accel.styleId = "MENU_ACCEL";
_accel.text = acc;
_accel.state = State.Parent;
if (_item.isSubmenu && !mainMenu)
_accel.alignment = Align.Right | Align.VCenter;
addChild(_accel);
}
trackHover = true;
@ -401,6 +489,15 @@ class MenuWidgetBase : ListWidget {
}
}
protected void handleMenuItemClick(MenuItem item) {
// precessing for CheckBox and RadioButton menus
if (item.type == MenuItemType.Check) {
item.checked = !item.checked;
} else if (item.type == MenuItemType.Radio) {
item.checked = true;
}
}
protected void onMenuItem(MenuItem item) {
debug Log.d("onMenuItem ", item.action.label);
if (_openedPopup !is null) {
@ -424,6 +521,10 @@ class MenuWidgetBase : ListWidget {
PopupWidget popup = cast(PopupWidget)parent;
if (popup)
popup.close();
handleMenuItemClick(item);
// this pointer now can be invalid - if popup removed
if (onMenuItemClickListenerCopy.assigned)
if (onMenuItemClickListenerCopy(item))
@ -566,6 +667,8 @@ class MainMenu : MenuWidgetBase {
deactivate();
handleMenuItemClick(item);
// this pointer now can be invalid - if popup removed
if (onMenuItemClickListenerCopy.assigned)
if (onMenuItemClickListenerCopy(item))