diff --git a/src/dlangui/core/events.d b/src/dlangui/core/events.d
index 2d5f745d..d861b2c4 100644
--- a/src/dlangui/core/events.d
+++ b/src/dlangui/core/events.d
@@ -171,6 +171,14 @@ class Action {
_id = newId;
return this;
}
+ /// compares id of this action with another action id
+ bool opEquals(int anotherActionId) const {
+ return _id == anotherActionId;
+ }
+ /// compares id of this action with another action id
+ bool opEquals(const Action action) const {
+ return _id == action._id;
+ }
/// sets label string resource id
@property Action label(string resourceId) {
_label = resourceId;
diff --git a/src/dlangui/core/stdaction.d b/src/dlangui/core/stdaction.d
index d9d57fbf..43b95875 100644
--- a/src/dlangui/core/stdaction.d
+++ b/src/dlangui/core/stdaction.d
@@ -30,29 +30,18 @@ enum StandardAction : int {
Ignore,
Open,
Save,
+ DiscardChanges,
}
-const Action ACTION_OK;
-const Action ACTION_CANCEL;
-const Action ACTION_YES;
-const Action ACTION_NO;
-const Action ACTION_CLOSE;
-const Action ACTION_ABORT;
-const Action ACTION_RETRY;
-const Action ACTION_IGNORE;
-const Action ACTION_OPEN;
-const Action ACTION_SAVE;
+const Action ACTION_OK = new Action(StandardAction.Ok, "ACTION_OK"c);
+const Action ACTION_CANCEL = new Action(StandardAction.Cancel, "ACTION_CANCEL"c);
+const Action ACTION_YES = new Action(StandardAction.Yes, "ACTION_YES"c);
+const Action ACTION_NO = new Action(StandardAction.No, "ACTION_NO"c);
+const Action ACTION_CLOSE = new Action(StandardAction.Close, "ACTION_CLOSE"c);
+const Action ACTION_ABORT = new Action(StandardAction.Abort, "ACTION_ABORT"c);
+const Action ACTION_RETRY = new Action(StandardAction.Retry, "ACTION_RETRY"c);
+const Action ACTION_IGNORE = new Action(StandardAction.Ignore, "ACTION_IGNORE"c);
+const Action ACTION_OPEN = new Action(StandardAction.Open, "ACTION_OPEN"c);
+const Action ACTION_SAVE = new Action(StandardAction.Save, "ACTION_SAVE"c);
+const Action ACTION_DISCARD_CHANGES = new Action(StandardAction.DiscardChanges, "ACTION_DISCARD_CHANGES"c);
-static this()
-{
- ACTION_OK = new Action(StandardAction.Ok, "ACTION_OK"c);
- ACTION_CANCEL = cast(immutable(Action)) new Action(StandardAction.Cancel, "ACTION_CANCEL"c);
- ACTION_YES = cast(immutable(Action)) new Action(StandardAction.Yes, "ACTION_YES"c);
- ACTION_NO = cast(immutable(Action)) new Action(StandardAction.No, "ACTION_NO"c);
- ACTION_CLOSE = cast(immutable(Action)) new Action(StandardAction.Close, "ACTION_CLOSE"c);
- ACTION_ABORT = cast(immutable(Action)) new Action(StandardAction.Abort, "ACTION_ABORT"c);
- ACTION_RETRY = cast(immutable(Action)) new Action(StandardAction.Retry, "ACTION_RETRY"c);
- ACTION_IGNORE = cast(immutable(Action)) new Action(StandardAction.Ignore, "ACTION_IGNORE"c);
- ACTION_OPEN = cast(immutable(Action)) new Action(StandardAction.Open, "ACTION_OPEN"c);
- ACTION_SAVE = cast(immutable(Action)) new Action(StandardAction.Save, "ACTION_SAVE"c);
-}
diff --git a/src/dlangui/widgets/tabs.d b/src/dlangui/widgets/tabs.d
index 699c1830..5a48c981 100644
--- a/src/dlangui/widgets/tabs.d
+++ b/src/dlangui/widgets/tabs.d
@@ -26,10 +26,18 @@ import dlangui.core.signals;
import dlangui.widgets.layouts;
import dlangui.widgets.controls;
+import std.algorithm;
+
+/// current tab is changed handler
interface TabHandler {
void onTabChanged(string newActiveTabId, string previousTabId);
}
+/// tab close button pressed handler
+interface TabCloseHandler {
+ void onTabClose(string tabId);
+}
+
/// tab item metadata
class TabItem {
@@ -86,6 +94,7 @@ class TabItemWidget : HorizontalLayout {
private ImageButton _closeButton;
private TabItem _item;
private bool _enableCloseButton;
+ Signal!TabCloseHandler onTabCloseListener;
@property TabItem tabItem() { return _item; }
@property TabControl tabControl() { return cast(TabControl)parent; }
this(TabItem item, bool enableCloseButton = true) {
@@ -119,6 +128,8 @@ class TabItemWidget : HorizontalLayout {
protected bool onClick(Widget source) {
if (source.compareId("CLOSE")) {
Log.d("tab close button pressed");
+ if (onTabCloseListener.assigned)
+ onTabCloseListener(_item.id);
}
return true;
}
@@ -217,6 +228,9 @@ class TabControl : WidgetGroupDefaultDrawing {
/// signal of tab change (e.g. by clicking on tab header)
Signal!TabHandler onTabChangedListener;
+ /// signal on tab close button
+ Signal!TabCloseHandler onTabCloseListener;
+
/// empty parameter list constructor - for usage by factory
this() {
this(null);
@@ -299,12 +313,29 @@ class TabControl : WidgetGroupDefaultDrawing {
/// remove tab
TabControl removeTab(string id) {
+ string nextId;
+ if (id.equal(_selectedTabId)) {
+ // current tab is being closed: remember next tab id
+ int nextIndex = getNextItemIndex(1);
+ if (nextIndex < 0)
+ nextIndex = getNextItemIndex(-1);
+ if (nextIndex >= 0)
+ nextId = _items[nextIndex].id;
+ }
int index = _items.indexById(id);
if (index >= 0) {
_children.remove(index + 1);
_items.remove(index);
+ if (id.equal(_selectedTabId))
+ _selectedTabId = null;
requestLayout();
}
+ if (nextId) {
+ index = _items.indexById(nextId);
+ if (index >= 0) {
+ selectTab(index, true);
+ }
+ }
return this;
}
@@ -329,6 +360,10 @@ class TabControl : WidgetGroupDefaultDrawing {
}
}
+ protected void onTabClose(string tabId) {
+ if (onTabCloseListener.assigned)
+ onTabCloseListener(tabId);
+ }
/// add new tab
TabControl addTab(TabItem item, int index = -1, bool enableCloseButton = false) {
@@ -337,6 +372,7 @@ class TabControl : WidgetGroupDefaultDrawing {
widget.parent = this;
widget.onClickListener = &onClick;
widget.setStyles(_tabButtonStyle, _tabButtonTextStyle);
+ widget.onTabCloseListener = &onTabClose;
_children.insert(widget, index);
updateTabs();
requestLayout();
@@ -440,6 +476,10 @@ class TabControl : WidgetGroupDefaultDrawing {
protected string _selectedTabId;
+ @property string selectedTabId() const {
+ return _selectedTabId;
+ }
+
void updateAccessTs() {
int index = _items.indexById(_selectedTabId);
if (index >= 0)
@@ -566,7 +606,7 @@ class TabHost : FrameLayout, TabHandler {
/// compound widget - contains from TabControl widget (tabs header) and TabHost (content pages)
-class TabWidget : VerticalLayout, TabHandler {
+class TabWidget : VerticalLayout, TabHandler, TabCloseHandler {
protected TabControl _tabControl;
protected TabHost _tabHost;
/// empty parameter list constructor - for usage by factory
@@ -579,6 +619,7 @@ class TabWidget : VerticalLayout, TabHandler {
_tabControl = new TabControl("TAB_CONTROL");
_tabHost = new TabHost("TAB_HOST", _tabControl);
_tabControl.onTabChangedListener.connect(this);
+ _tabControl.onTabCloseListener.connect(this);
styleId = STYLE_TAB_WIDGET;
addChild(_tabControl);
addChild(_tabHost);
@@ -587,6 +628,13 @@ class TabWidget : VerticalLayout, TabHandler {
/// signal of tab change (e.g. by clicking on tab header)
Signal!TabHandler onTabChangedListener;
+ /// signal on tab close button
+ Signal!TabCloseHandler onTabCloseListener;
+
+ protected override void onTabClose(string tabId) {
+ if (onTabCloseListener.assigned)
+ onTabCloseListener(tabId);
+ }
protected override void onTabChanged(string newActiveTabId, string previousTabId) {
// forward to listener
diff --git a/src/dlangui/widgets/tree.d b/src/dlangui/widgets/tree.d
index 0a6927dc..41b1877c 100644
--- a/src/dlangui/widgets/tree.d
+++ b/src/dlangui/widgets/tree.d
@@ -738,9 +738,15 @@ class TreeWidgetBase : ScrollWidget, OnTreeContentChangeListener, OnTreeStateCh
}
}
+ void clearSelection() {
+ _tree.selectItem(null);
+ }
+
void selectItem(TreeItem item, bool makeVisible = true) {
- if (!item)
+ if (!item) {
+ clearSelection();
return;
+ }
_tree.selectItem(item);
if (makeVisible)
makeItemVisible(item);
diff --git a/views/res/i18n/std_en.ini b/views/res/i18n/std_en.ini
index 8e1611ce..253bc157 100644
--- a/views/res/i18n/std_en.ini
+++ b/views/res/i18n/std_en.ini
@@ -10,3 +10,4 @@ ACTION_RETRY=Retry
ACTION_IGNORE=Ignore
ACTION_OPEN=Open
ACTION_SAVE=Save
+ACTION_DISCARD_CHANGES=Discard changes
diff --git a/views/res/theme_default.xml b/views/res/theme_default.xml
index f18ac6cf..b7460ee0 100644
--- a/views/res/theme_default.xml
+++ b/views/res/theme_default.xml
@@ -111,6 +111,7 @@
>