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 patterns 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.

Constant Summary

Constants inherited from Struct

Struct::CACHE

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Struct

new

Constructor Details

#initialize(widget, major) ⇒ WidgetMajorMode

Returns a new instance of WidgetMajorMode.



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 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(500) # this may be too small still
  self.action_history = SizedArray.new(100)
  self.stack = []
  self.minors = []

  establish_tag
  use(*self.major.minors)
  listen

  # in case we had some keymap updates, they must have been registered by
  # now, since otherwise we wouldn't be here.
  # This will be called around 3 times per startup.
  Event.done_yet?
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



197
198
199
# File 'lib/ver/widget_major_mode.rb', line 197

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

#bind(pattern, &block) ⇒ Object

Shortcut to bind a pattern on the associated widget.



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

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

#bind_key(key) ⇒ Object



48
49
50
# File 'lib/ver/widget_major_mode.rb', line 48

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

#bound_keysObject



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

def bound_keys
  major.bound_keys
end

#establish_tagObject



231
232
233
234
235
236
# File 'lib/ver/widget_major_mode.rb', line 231

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

#fake(input) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ver/widget_major_mode.rb', line 66

def fake(input)
  input.scan(/<[\w-]+>|[^<>]/) do |name|
    # get the widget that currently has focus, this might not be self.
    path = Tk::Focus.focus
    widget = Tk.pathname_to_widget(path)

    if widget == self.widget
      on_event(Event[name])
      Tk.update
    else
      widget.type(name)
    end
  end
end

#forget(*minors) ⇒ Object



62
63
64
# File 'lib/ver/widget_major_mode.rb', line 62

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

#handle_reader(event) ⇒ Object

ignore event for now, it might be needed later



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/ver/widget_major_mode.rb', line 126

def handle_reader(event)
  read_amount = self.read_amount

  if stack.size >= read_amount
    stack.clear

    # before we call, we reset the state in case the reader adds a new reader.
    reader = self.reader
    self.reader = self.read_amount = nil
    reader.call(*event_history.last(read_amount))
    true
  else
    false
  end
end

#inspectObject



221
222
223
224
225
226
227
228
229
# File 'lib/ver/widget_major_mode.rb', line 221

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

#listenObject



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

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

#message(*args) ⇒ Object



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

def message(*args)
  if widget.respond_to?(:message)
    widget.message(*args)
  else
    VER.message(*args)
  end
end

#nameObject



205
206
207
# File 'lib/ver/widget_major_mode.rb', line 205

def name
  major.name
end

#on_event(event) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/ver/widget_major_mode.rb', line 81

def on_event(event)
  VER.touch
  stack << event.pattern

  # replace with our subset
  event = Event.new(event.pattern, event.keysym, event.unicode)
  event_history << event

  return handle_reader(event) if reader && read_amount

  result = resolve(stack)
  case result
  when Incomplete
    message result.to_s(widget)
    # don't do anything yet...
  when Impossible
    warn result.to_s
    stack.clear
  else
    stack.clear
    message ''
    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)


142
143
144
145
146
147
# File 'lib/ver/widget_major_mode.rb', line 142

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



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

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



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

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



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

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

#replacing(other) ⇒ Object



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

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(pattern) ⇒ Object



149
150
151
# File 'lib/ver/widget_major_mode.rb', line 149

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

#synchronizeObject



187
188
189
190
191
192
193
194
195
# File 'lib/ver/widget_major_mode.rb', line 187

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

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

#tagObject



209
210
211
# File 'lib/ver/widget_major_mode.rb', line 209

def tag
  major.tag
end

#to_camel_caseObject



217
218
219
# File 'lib/ver/widget_major_mode.rb', line 217

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

#to_hashObject



201
202
203
# File 'lib/ver/widget_major_mode.rb', line 201

def to_hash
  [major.keymap, *minors].inject{|h, m| h.merge(m.keymap) }
end

#to_tclObject



213
214
215
# File 'lib/ver/widget_major_mode.rb', line 213

def to_tcl
  widget.tk_pathname
end

#use(*minors) ⇒ Object



52
53
54
55
56
57
58
59
60
# File 'lib/ver/widget_major_mode.rb', line 52

def use(*minors)
  minors.each do |name|
    minor = MinorMode[name]
    next if self.minors.any?{|m| m.name == minor.name }
    self.minors << minor
  end

  synchronize
end

#warn(*args) ⇒ Object



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

def warn(*args)
  if widget.respond_to?(:warn)
    widget.warn(*args)
  else
    VER.warn(*args)
  end
end