Class: VER::WidgetMajorMode

Inherits:
Struct
  • Object
show all
Includes:
Keymap::Results
Defined in:
lib/ver/widget_major_mode.rb

Overview

The WidgetMajorMode associates a widget with a major mode. It keeps a limited history of the events that arrive at [on_event]. It also maintains a stack of the event sequences and tries to match against the keymaps of the major and minor modes.

This class has its own list of minor modes, which can be modified and may differ from the minor modes of the original major mode.

It is responsible to keep the keymaps of major and minor modes functioning by ensuring that they are actually bound to the tag of the major mode.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(widget, major) ⇒ WidgetMajorMode

Returns a new instance of WidgetMajorMode.



17
18
19
20
21
22
23
24
25
26
27
28
# File 'lib/ver/widget_major_mode.rb', line 17

def initialize(widget, major)
  self.widget = widget
  self.major = MajorMode[major]
  self.event_history = SizedArray.new(100)
  self.action_history = SizedArray.new(100)
  self.stack = []
  self.minors = []

  establish_tag
  use(*self.major.minors)
  listen
end

Instance Attribute Details

#action_historyObject

Returns the value of attribute action_history

Returns:

  • (Object)

    the current value of action_history



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def action_history
  @action_history
end

#event_historyObject

Returns the value of attribute event_history

Returns:

  • (Object)

    the current value of event_history



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def event_history
  @event_history
end

#majorObject

Returns the value of attribute major

Returns:

  • (Object)

    the current value of major



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def major
  @major
end

#minorsObject

Returns the value of attribute minors

Returns:

  • (Object)

    the current value of minors



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def minors
  @minors
end

#read_amountObject

Returns the value of attribute read_amount

Returns:

  • (Object)

    the current value of read_amount



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def read_amount
  @read_amount
end

#readerObject

Returns the value of attribute reader

Returns:

  • (Object)

    the current value of reader



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def reader
  @reader
end

#stackObject

Returns the value of attribute stack

Returns:

  • (Object)

    the current value of stack



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def stack
  @stack
end

#widgetObject

Returns the value of attribute widget

Returns:

  • (Object)

    the current value of widget



12
13
14
# File 'lib/ver/widget_major_mode.rb', line 12

def widget
  @widget
end

Instance Method Details

#actionsObject



155
156
157
# File 'lib/ver/widget_major_mode.rb', line 155

def actions
  major.actions + minors.map{|minor| minor.actions }.flatten
end

#bind(sequence, &block) ⇒ Object

Shortcut to bind a sequence on the associated widget.



39
40
41
# File 'lib/ver/widget_major_mode.rb', line 39

def bind(sequence, &block)
  widget.bind(sequence, &block)
end

#bind_key(key) ⇒ Object



43
44
45
# File 'lib/ver/widget_major_mode.rb', line 43

def bind_key(key)
  major.bind_key(key)
end

#bound_keysObject



34
35
36
# File 'lib/ver/widget_major_mode.rb', line 34

def bound_keys
  major.bound_keys
end

#establish_tagObject



185
186
187
188
189
190
# File 'lib/ver/widget_major_mode.rb', line 185

def establish_tag
  tags = widget.bindtags
  specific = tags[0, 1]
  general = tags[-3..-1]
  widget.bindtags(*specific, major.tag, *general)
end

#forget(*minors) ⇒ Object



57
58
59
# File 'lib/ver/widget_major_mode.rb', line 57

def forget(*minors)
  self.minors -= minors.map{|name| MinorMode[name] }
end

#handle_reader(event) ⇒ Object

ignore event for now, it might be needed later



88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ver/widget_major_mode.rb', line 88

def handle_reader(event)
  if stack.size >= read_amount
    stack.clear
    handled = true
    reader.call(*event_history.last(read_amount))
  else
    handled = false
  end
ensure
  self.read_amount = self.reader = nil if handled
end

#inspectObject



175
176
177
178
179
180
181
182
183
# File 'lib/ver/widget_major_mode.rb', line 175

def inspect
  out = ['#<Ver::WidgetMajorMode']
  { major: major.name,
    minors: minors.map{|m| m.to_sym },
    event_history: event_history.map{|h| h[:keysym] },
    stack: stack,
  }.each{|k,v| out << "#{k}=#{v.inspect}" }
  out.join(' ') << '>'
end

#listenObject



30
31
32
# File 'lib/ver/widget_major_mode.rb', line 30

def listen
  VER.root.bind('<<PluginLoaded>>'){|event| synchronize }
end

#nameObject



159
160
161
# File 'lib/ver/widget_major_mode.rb', line 159

def name
  major.name
end

#on_event(event) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ver/widget_major_mode.rb', line 61

def on_event(event)
  stack << event.sequence
  event_history << {
    sequence: event.sequence,
    keysym: event.keysym,
    unicode: event.unicode
  }

  return handle_reader(event) if reader && read_amount

  case result = resolve(stack)
  when Incomplete
    VER.message result.to_s
    # don't do anything yet...
  when Impossible
    VER.message result.to_s
    stack.clear
  else
    stack.clear
    mode, action = result
    widget_event = WidgetEvent.new(widget, event)
    action.call(widget_event)
    action_history << [widget_event, *result]
  end
end

#read(amount, &reader) ⇒ Object

Raises:

  • (ArgumentError)


100
101
102
103
104
105
# File 'lib/ver/widget_major_mode.rb', line 100

def read(amount, &reader)
  amount = amount.to_int
  raise ArgumentError, "amount must be greater than 0" unless amount > 0
  self.read_amount = amount
  self.reader = reader
end

#replace_minor(old, new) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/ver/widget_major_mode.rb', line 129

def replace_minor(old, new)
  old, new = MinorMode[old], MinorMode[new]

  minors.dup.each do |minor|
    if minor == old
      new.replaces self, old do
        minors[minors.index(old)] = new
      end
    else
      minor.replace_parent(self, old, new)
    end
  end

  synchronize
end

#replaced_by(other) ⇒ Object



117
118
119
120
121
# File 'lib/ver/widget_major_mode.rb', line 117

def replaced_by(other)
  Tk::Event.generate(widget, "<<LeaveMode>>", data: name)
  Tk::Event.generate(widget, "<<LeaveMajorMode>>", data: name)
  Tk::Event.generate(widget, "<<LeaveMajorMode#{to_camel_case}>>", data: name)
end

#replaces(other) ⇒ Object



111
112
113
114
115
# File 'lib/ver/widget_major_mode.rb', line 111

def replaces(other)
  other.replaced_by(self) if other
  yield if block_given?
  self.replacing(other)
end

#replacing(other) ⇒ Object



123
124
125
126
127
# File 'lib/ver/widget_major_mode.rb', line 123

def replacing(other)
  Tk::Event.generate(widget, "<<EnterMajorMode#{to_camel_case}>>", data: name)
  Tk::Event.generate(widget, "<<EnterMajorMode>>", data: name)
  Tk::Event.generate(widget, "<<EnterMode>>", data: name)
end

#resolve(sequence) ⇒ Object



107
108
109
# File 'lib/ver/widget_major_mode.rb', line 107

def resolve(sequence)
  major.resolve(sequence, minors)
end

#synchronizeObject



145
146
147
148
149
150
151
152
153
# File 'lib/ver/widget_major_mode.rb', line 145

def synchronize
  major.keymap.keys.each do |key|
    major.bind_key(key)
  end

  minors.each do |minor|
    minor.synchronize_recursively(self)
  end
end

#tagObject



163
164
165
# File 'lib/ver/widget_major_mode.rb', line 163

def tag
  major.tag
end

#to_camel_caseObject



171
172
173
# File 'lib/ver/widget_major_mode.rb', line 171

def to_camel_case
  major.name.to_s.split('_').map{|e| e.capitalize}.join
end

#to_tclObject



167
168
169
# File 'lib/ver/widget_major_mode.rb', line 167

def to_tcl
  widget.tk_pathname
end

#use(*minors) ⇒ Object



47
48
49
50
51
52
53
54
55
# File 'lib/ver/widget_major_mode.rb', line 47

def use(*minors)
  minors.each do |name|
    minor = MinorMode[name]
    self.minors << minor
  end

  self.minors.uniq!
  synchronize
end