Module: Ruber::GuiStatesHandler

Included in:
MainWindow, OutputWidget
Defined in:
lib/ruber/gui_states_handler.rb

Overview

Module which provides a mechanism to enable/disable actions basing on whether some ‘states’ are true or false. This functionality is similar to that provided by the KDE XML GUI framework, but much more flexible.

A state is simply a string to which a true or false value is associated. For example, the value associated with a state called "current_document_modified" should be set to true whenever the current document becomes modified and to false when it’s saved. Through the rest of this documentation, the expression the value of the state will mean the true or false value associated with the state.

The system works this way: an action is registered using the register_action_handler method. To do so, a block (called a handler) and a list of states the action depends on are supplied.

The value of a state is modified using the change_state method. That method will call the handlers of every action depending on the changed state, passing it a list of all known states, and enables or disable them according to the value returned by the block.

Whenever this module is included in a class derived from Qt::Object, it will define a signal with the following signature ui_state_changed(QString, bool), which will be emitted from the state_changed method. The signal won’t be defined if the module extends an object (even if it is Qt::Object).

Note: before using any of the functionality provided by this module, you must call the initialize_states_handler method. This is usually done in the initialize method of the including class.

Defined Under Namespace

Classes: Data

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(other) ⇒ Object

Override of Module#included which is called whenever the module is included in another module or class.

If other is a class derived from Qt::Object, it defines the ui_state_changed(QString, bool) signal.



67
68
69
# File 'lib/ruber/gui_states_handler.rb', line 67

def self.included other
  other.send :signals, 'ui_state_changed(QString, bool)' if other.ancestors.include?(Qt::Object)
end

Instance Method Details

#change_state(state, value) ⇒ Object Also known as: set_state

Sets the value associated with the state state to value, then calls the handlers of all actions depending on state and enables or disables them according to the returned value. If self is an instance of Qt::Object, it also emits the ui_state_changed(QString, bool) signal, passing state and value (converted to bool) as arguments.

state can be either a string or symbol (in this case, it’ll be converted to string). value can be any value. It will be converted to true or false according to the usual ruby rules before being used.



198
199
200
201
202
203
204
205
206
# File 'lib/ruber/gui_states_handler.rb', line 198

def change_state state, value
  state = state.to_s
  value = value.to_bool
  @gui_state_handler_states[state] = value
  @gui_state_handler_handlers[state].each do |d| 
    d.action.enabled = d.handler.call @gui_state_handler_states
  end
  emit ui_state_changed state, value if self.is_a?(Qt::Object)
end

#register_action_handler(action, states, options = {:check => true}, &blk) ⇒ Object

Makes an action known to the system, telling it on which states it depends and which handler should be used to decide whether the action should be enabled or not.

action is a Qt::Action or derived object. states is an array of strings and/or symbols or a single string or symbol. Each string/symbol represents the name of a state action depends on (that is, a state whose value concurs in deciding whether action should be enabled or not).

blk is the handler associated with action. It takes a single argument, which will be a hash having the names of the states as keys and their values as values, and returns a true value if, basing on the values of the states, action should be enabled and a false value if it shouldn’t.

option contains some options. One is :check: if true, the handler will be called (and the action enabled or disabled) immediately after registering it; if it’s false, nothing will be done until one of the states in states changes. The other option is :extra_id. If not nil, it is an extra value used to identify the action (see remove_action_handler_for for more information).

If the block is not given and states is a string, a symbol or an array with only one element, then a default handler is generated (if states is an array with more than one element, ArgumentError is raised). The default handler returns the same value as the state value. If the only state starts with a !, instead, the leading exclamation mark is removed from the name of the state and the generated handler returns the opposite of the state value (that is, returns true if the state is false and vice versa).

Note: even if states can be specified as symbols, they’re actually always converted to strings.

Examples

In all the following examples, action_handler is an instance of a class which includes this module.

This registers an handler for an action which depends on the two states ‘a’ and ‘b’. The action should be enabled when ‘a’ is true and ‘b’ is false or when ‘a’ is false, regardless of ‘b’.

action_handler.register_action_handler KDE::Action.new(nil), ['a', 'b'] do |states|
  if states['a'] and !states['b'] then true
  elsif !states['a'] then true
  else false
  end
end

This registers an action depending only on the state ‘a’ which should be enabled when ‘a’ is true and disabled when ‘a’ is false.

action_handler.register_action_handler KDE::Action.new(nil), ['a'] do |states|
  states['a']
end

Here we could also have passed simply the string ‘a’ as second argument. Even easier, we could have omitted the block, relying on the default handler that would have been generated:

action_handler.register_action_handler KDE::Action.new(nil), 'a'

Here, still using the default handler, register an handler for an action which depends only on the state ‘a’ but needs to be enabled when ‘a’ is false and disabled when it’s true.

action_handler.register_action_handler KDE::Action.new(nil), '!a'


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/ruber/gui_states_handler.rb', line 136

def register_action_handler action, states, options = {:check => true}, &blk
  states = Array(states)
  states = states.map &:to_s
  if !blk and states.size > 1
    raise ArgumentError, "If more than one state is supplied, a block is needed" 
  elsif !blk
    if states[0].start_with? '!'
      states[0] = states[0][1..-1]
      blk = Proc.new{|s| !s[states[0]]}
    else blk = Proc.new{|s| s[states[0]]}
    end
  end
  data = Data.new(action, blk, options[:extra_id])
  states.each{|s| @gui_state_handler_handlers[s.to_s] << data}
  if options[:check]
    action.enabled = blk.call @gui_state_handler_states
  end
end

#remove_action_handler_for(action, extra_id = nil) ⇒ Object

Remove the existing handlers for actions matching action and extra_id.

action may be either a Qt::Action or a string. If it is a Qt::Action, then only the handlers for that object will be removed (actually, there usually is only one handler).

If action is a string, all handlers for actions whose object_name is action will be removed. In this case, the extra_id parameter can be used narrow the list of actions to remove (for example, if there are more than one action with the same name but different extra_ids). If extra_id isn’t nil, then only actions whose object_name is action and which were given an extra_id equal to extra_id will be removed.



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/ruber/gui_states_handler.rb', line 170

def remove_action_handler_for action, extra_id = nil
  if action.is_a? KDE::Action
    @gui_state_handler_handlers.each_value do |v|
      v.delete_if{|d| d.action.same? action}
    end
  elsif extra_id
    @gui_state_handler_handlers.each_value do |v|
      v.delete_if{|d| d.action.object_name == action and d.extra_id == extra_id}
    end
  else
    @gui_state_handler_handlers.each_value do |v|
      v.delete_if{|d| d.action.object_name == action}
    end
  end
  @gui_state_handler_handlers.delete_if{|k, v| v.empty?}
end

#state(name) ⇒ Object Also known as: gui_state

Returns the value associated with the state name. If the value of the state has never been set using change_state, nil will be returned.



213
214
215
# File 'lib/ruber/gui_states_handler.rb', line 213

def state name
  @gui_state_handler_states[name.to_s]
end