diff --git a/src/dlangui/core/events.d b/src/dlangui/core/events.d index 3f365a47..33be614f 100644 --- a/src/dlangui/core/events.d +++ b/src/dlangui/core/events.d @@ -60,10 +60,14 @@ class ActionState { ActionState clone() const { return new ActionState(enabled, visible, checked); } + override bool opEquals(Object obj) { + if (auto other = cast(ActionState)obj) { + return enabled == other.enabled && visible == other.visible && checked == other.checked; + } + return false; + } } -immutable ACTION_ID_STATE_REQUEST = -1; - /// action is __gshared const(ActionState) ACTION_STATE_DEFAULT_ENABLED; __gshared const(ActionState) ACTION_STATE_DEFAULT_DISABLE; @@ -98,39 +102,30 @@ class Action { /// optional object parameter protected Object _objectParam; + protected ActionState _state; + protected ActionState _defaultState; /// default state for action if action state lookup failed @property const(ActionState) defaultState() const { return _defaultState ? _defaultState : ACTION_STATE_DEFAULT_ENABLED; } - /// default state for action if action state lookup failed @property Action defaultState(ActionState s) { _defaultState = s; return this; } - - /// returns true if action is state request - @property bool isStateRequest() const { - return id == ACTION_ID_STATE_REQUEST; + /// action state + @property const(ActionState) state() const { return _state ? _state : (_defaultState ? _defaultState : ACTION_STATE_DEFAULT_ENABLED); } + /// update action state (for non-const action) + @property Action state(const ActionState s) { + if (_state != s) + _state = s.clone(); + return this; } - - /// if this action is request of UI state for another action, returns ID of action state is requested for - @property int requestedActionId() const { - return isStateRequest ? cast(int)_longParam : 0; - } - /// if this action is request of UI state for another action, returns state (default if not changed while request handling by UI components) - @property const(ActionState) requestedActionState() const { - assert(isStateRequest); - return cast(ActionState)_objectParam; - } - /// if this action is request of UI state for another action, returns state (default if not changed while request handling by UI components) - @property const(Action) requestedActionState(ActionState s) const { - // hack: it's ok to replace action state in const ACTION_ID_STATE_REQUEST action - assert(isStateRequest); - Action nonconstThis = cast(Action)this; - nonconstThis._objectParam = cast(Object)s.clone(); - return this; - } - /// create state request action for current action - Action createStateRequest() { - return (new Action(ACTION_ID_STATE_REQUEST)).longParam(_id).objectParam(defaultState.clone()); + /// update action state (can be changed even for const objects) + @property const(Action) state(const ActionState s) const { + if (_state != s) { + // hack + Action nonConstThis = cast(Action) this; + nonConstThis._state = s.clone(); + } + return this; } /// returns optional string parameter @@ -169,6 +164,8 @@ class Action { _id = a._id; _label = a._label; _iconId = a._iconId; + _state = a._state.clone(); + _defaultState = a._defaultState.clone(); _accelerators.length = a._accelerators.length; for(int i = 0; i < _accelerators.length; i++) _accelerators[i] = a._accelerators[i]; diff --git a/src/dlangui/platforms/common/platform.d b/src/dlangui/platforms/common/platform.d index f21d5307..04bee0a4 100644 --- a/src/dlangui/platforms/common/platform.d +++ b/src/dlangui/platforms/common/platform.d @@ -364,6 +364,7 @@ class Window { } if (animationActive) scheduleAnimation(); + _actionsUpdateRequested = false; } /// after drawing, call to schedule redraw if animation is active @@ -401,6 +402,8 @@ class Window { newFocus.setState(State.Focused); } _focusedWidget = newFocus; + // after focus change, ask for actions update automatically + requestActionsUpdate(); } return _focusedWidget; } @@ -611,6 +614,38 @@ class Window { return false; } + /// dispatch action to main widget + bool dispatchActionStateRequest(const Action action, Widget sourceWidget = null) { + // try to handle by source widget + if(sourceWidget && isChild(sourceWidget)) { + if (sourceWidget.handleActionStateRequest(action)) + return true; + sourceWidget = sourceWidget.parent; + } + Widget focus = focusedWidget; + // then offer action to focused widget + if (focus && isChild(focus)) { + if (focus.handleActionStateRequest(action)) + return true; + focus = focus.parent; + } + // then offer to parent chain of source widget + while (sourceWidget && isChild(sourceWidget)) { + if (sourceWidget.handleActionStateRequest(action)) + return true; + sourceWidget = sourceWidget.parent; + } + // then offer to parent chain of focused widget + while (focus && isChild(focus)) { + if (focus.handleActionStateRequest(action)) + return true; + focus = focus.parent; + } + if (_mainWidget) + return _mainWidget.handleActionStateRequest(action); + return false; + } + /// post event to handle in UI thread (this method can be used from background thread) void postEvent(CustomEvent event) { // override to post event into window message queue @@ -833,6 +868,16 @@ class Window { /// close window abstract void close(); + protected bool _actionsUpdateRequested; + /// set action update request flag, will be cleared after redraw + void requestActionsUpdate() { + _actionsUpdateRequested = true; + } + + @property bool actionsUpdateRequested() { + return _actionsUpdateRequested; + } + /// Show message box with specified title and message void showMessageBox(UIString title, UIString message, const (Action)[] actions = [ACTION_OK], int defaultActionIndex = 0, bool delegate(const Action result) handler = null) { MessageBox dlg = new MessageBox(title, message, this, actions, defaultActionIndex, handler); diff --git a/src/dlangui/widgets/widget.d b/src/dlangui/widgets/widget.d index 46c0088f..005a8891 100644 --- a/src/dlangui/widgets/widget.d +++ b/src/dlangui/widgets/widget.d @@ -626,7 +626,24 @@ class Widget { @property void action(const Action action) { _action = action.clone; } /// action to emit on click @property void action(Action action) { _action = action; } - + /// ask for update state of some action (unles force=true, checks window flag + void updateActionState(Action a, bool force = false) { + if (Window w = window) { + if (!force && !w.actionsUpdateRequested()) + return; + if (w.dispatchActionStateRequest(a, this)) { + // state is updated + } else { + a.state = a.defaultState; + } + } + } + /// call to update state for action (if action is assigned for widget) + void updateActionState(bool force = false) { + if (!_action) + return; + updateActionState(_action, force); + } protected bool _focusGroup; /***************************************** @@ -906,6 +923,10 @@ class Widget { bool handleAction(const Action a) { return false; } + /// override to handle specific actions state (e.g. change enabled state for supported actions) + bool handleActionStateRequest(const Action a) { + return false; + } /// call to dispatch action bool dispatchAction(const Action a) {