Module: Clevic::ActionBuilder

Included in:
TableView
Defined in:
lib/clevic/action_builder.rb,
lib/clevic/qt/action_builder.rb,
lib/clevic/swing/action_builder.rb

Overview

This module is included in a class to make the construction of collections of actions more rubyish. It must have

- an add_action method 
- separator, which returns something which is_a? Separator
- create_action( &block ), which creates an Action object
- action_method_or_block( action, options, &block ) which handles events

Menus are generally made up of a collection of actions.

Once included, it’s intended to be called as follows:

def some_setup_method_or_other
  build_actions do
    list :edit do
      #~ new_action :action_cut, 'Cu&t', :shortcut => Qt::KeySequence::Cut
      action :action_copy, '&Copy', :shortcut => Qt::KeySequence::Copy, :method => :copy_current_selection
      action :action_paste, '&Paste', :shortcut => Qt::KeySequence::Paste, :method => :paste
      separator
      action :action_ditto, '&Ditto', :shortcut => 'Ctrl+\'', :method => :ditto, :tool_tip => 'Copy same field from previous record'
      action :action_ditto_right, 'Ditto R&ight', :shortcut => 'Ctrl+]', :method => :ditto_right, :tool_tip => 'Copy field one to right from previous record'
      action :action_ditto_left, '&Ditto L&eft', :shortcut => 'Ctrl+[', :method => :ditto_left, :tool_tip => 'Copy field one to left from previous record'
      action :action_insert_date, 'Insert Date', :shortcut => 'Ctrl+;', :method => :insert_current_date
      action :action_open_editor, '&Open Editor', :shortcut => 'F4', :method => :open_editor
      separator
      action :action_row, 'New Ro&w', :shortcut => 'Ctrl+N', :method => :row
      action :action_refresh, '&Refresh', :shortcut => 'Ctrl+R', :method => :refresh
      action :action_delete_rows, 'Delete Rows', :shortcut => 'Ctrl+Delete', :method => :delete_rows

      if $options[:debug]
        action :action_dump, 'D&ump', :shortcut => 'Ctrl+Shift+D' do
          puts model.collection[current_index.row].inspect
        end
      end
    end

    separator
  end
end

Or you can pass a parameter to the block if you need access to surrounding variables:

build_actions do |ab|
  ab.list :edit do
    #~ new_action :action_cut, 'Cu&t', :shortcut => Qt::KeySequence::Cut
    ab.action :action_copy, '&Copy', :shortcut => Qt::KeySequence::Copy, :method => :copy_current_selection
  end
end

If the including class defines a method called action_triggered( &block ), it can be used to wrap the code triggered by actions. That way, the including class can catch exceptions and things like that.

def action_triggered( &block )
  catch :something_happened do
    yield
  end
end

If this method is not defined, it will be created in the including class as an empty wrapper.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(including_module) ⇒ Object

raise a RuntimeError if the including class/module does not define add_action



61
62
63
64
65
66
67
68
# File 'lib/clevic/action_builder.rb', line 61

def self.included( including_module )
  shortlist = including_module.instance_methods.grep /action/i
  # add_action is actually an method_missing lookup for addAction, so
  # search for both.
  unless shortlist.any? {|x| %w{add_action addAction}.include?( x )}
    raise NotImplementedError, "#{including_module.class.name} must have an add_action method"
  end
end

Instance Method Details

#action(name_or_action, text = nil, options = {}, &block) ⇒ Object

Create a new Action and

  1. pass it to add_action

  2. add it to the collect_actions collection.

The block takes predence over options, which is a method on self to be called. Option keys can be any method in Action, ie :tool_tip, :shortcut, :status_tip etc. A value for :shortcut is automatically passed to create_key_sequence



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/clevic/action_builder.rb', line 114

def action( name_or_action, text = nil, options = {}, &block )
  if name_or_action.is_a? Action
    add_action( name_or_action )
  else
    name = name_or_action
    if options.has_key?( :method ) && !block.nil?
      raise "you can't specify both :method and a block"
    end

    create_action do |action|
      action.name = name.to_s
      action.text = text
      options.each do |k,v|
        next if k == :method
        if k == :shortcut
          action.shortcut = v
        else
          action.send( "#{k.to_s}=", v )
        end
      end

      # add action
      add_action action

      # add actions for list. Yes, it's a side-effect.
      # TODO is there a better way to do this?
      collect_actions << action

      action_method_or_block( action, options, &block )
    end
  end
end

#action_method_or_block(action, options, &block) ⇒ Object

set up the code to be executed when an action is triggered,



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/clevic/qt/action_builder.rb', line 23

def action_method_or_block( qt_action, options, &block )
  signal_name = "triggered(#{options.has_key?( :checkable ) ? 'bool' : ''})"

  # connect the action to some code
  if options.has_key?( :method )
    qt_action.connect SIGNAL( signal_name ) do |active|
      action_triggered do
        send_args = [ options[:method], options.has_key?( :checkable ) ? active : nil ].compact
        send( *send_args )
      end
    end
  else
    unless block.nil?
      action_triggered do
        qt_action.connect SIGNAL( signal_name ) do |active|
          # need this rescue here because otherwise Qt
          # doesn't catch it and the whole app comes down
          begin
            yield( active )
          rescue
            # TODO how to display this in the UI?
            puts $!.message
            puts $!.backtrace
          end
        end
      end
    end
  end
end

#build_actions(&block) ⇒ Object

Outer block for the build process.



71
72
73
74
75
76
77
78
# File 'lib/clevic/action_builder.rb', line 71

def build_actions( &block )
  raise 'a block must be present' if block.nil?
  if block.arity == -1
    instance_eval &block
  else
    yield self
  end
end

#create_action(&block) ⇒ Object



13
14
15
# File 'lib/clevic/qt/action_builder.rb', line 13

def create_action( &block )
  Qt::Action.new( parent, &block )
end

#create_key_sequence(sequence) ⇒ Object

TODO move this into Action, like the swing adapter



18
19
20
# File 'lib/clevic/qt/action_builder.rb', line 18

def create_key_sequence( sequence )
  Qt::KeySequence.new( sequence )
end

#group_namesObject



80
81
82
# File 'lib/clevic/action_builder.rb', line 80

def group_names
  @group_names ||= []
end

#list(group_name) {|_self| ... } ⇒ Object

Create and return a list of actions. The actions are grouped together, ie live together on the menu with a separator between groups. A method called “#group_name_actions” will be added to self, which will return the set of framework-specific Action instances created in the block.

Yields:

  • (_self)

Yield Parameters:



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/clevic/action_builder.rb', line 88

def list( group_name, &block )
  @group_name = group_name
  group_names << group_name
  unless respond_to?( "#{group_name.to_s}_actions" )
    self.class.send( :define_method, "#{group_name.to_s}_actions" ) do
      eval "@#{group_name.to_s}_actions"
    end
  end
  self.collect_actions = []

  yield( self )
  # copy actions to the right instance variable
  eval "@#{group_name.to_s}_actions = collect_actions"

  # reset these, just for cleanliness
  @group_name = nil
  self.collect_actions = []
end

#separatorObject

Create a new separator and add a new separator.



5
6
7
8
9
10
11
# File 'lib/clevic/qt/action_builder.rb', line 5

def separator
  Qt::Action.new( parent ) do |action|
    action.separator = true
    add_action action
    collect_actions << action
  end
end