Class: RubyCurses::Container

Inherits:
Widget show all
Defined in:
lib/rbcurse/core/widgets/rcontainer.rb

Overview

This is an attempt at having a container which can contain multiple widgets without being a form itself. Having forms within forms complicates code too much, esp cursor positioning. e.g. tabbedpane

Instance Attribute Summary collapse

Attributes inherited from Widget

#_object_created, #col_offset, #cols_panned, #config, #curpos, #focussed, #form, #id, #key_label, #parent_component, #row_offset, #rows_panned, #state

Instance Method Summary collapse

Methods inherited from Widget

#action_manager, #changed, #click, #color_pair, #command, #destroy, #enter, #event_list, #focus, #get_preferred_size, #getvalue, #getvalue_for_paint, #height, #height=, #hide, #leave, #modified?, #move, #override_graphic, #process_key, #remove, #repaint_all, #repaint_required, #rowcol, #set_buffer_modified, #set_buffering, #set_form, #set_modified, #setformrowcol, #setrowcol, #show, #text_variable, #unbind_key, #width, #width=

Methods included from Io

#__create_footer_window, #clear_this, #get_file, #print_this, #rb_getchar, #rb_gets, #rbgetstr, #warn

Methods included from Utils

#OLDdefine_key, #_process_key, #bind_key, #bind_keys, #clean_string!, #define_key, #define_prefix_command, #display_app_help, #get_attrib, #get_color, #keycode_tos, #last_line, #one_line_window, #parse_formatted_text, #print_key_bindings, #repeatm, #run_command, #shell_out, #shell_output, #suspend, #view, #wrap_text

Methods included from ConfigSetup

#cget, #config_setup, #configure, #variable_set

Methods included from EventHandler

#bind, #fire_handler, #fire_property_change

Constructor Details

#initialize(form = nil, config = {}, &block) ⇒ Container

Returns a new instance of Container.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 47

def initialize form=nil, config={}, &block
  @suppress_borders = false
  @row_offset = @col_offset = 1
  @_events ||= []
  @stack = true
  @positioning = :stack
  super
  @focusable = true
  @editable = false
  @components = [] # all components
  @focusables = [] # focusable components, makes checks easier

  init_vars
end

Instance Attribute Details

#current_componentObject (readonly)

Returns the value of attribute current_component.



45
46
47
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 45

def current_component
  @current_component
end

Instance Method Details

#add(*items) ⇒ Object Also known as: add_widget

NOTE: since we are handling the traversal, we delink the object from any form’s widgets array that might have been added. Whenever a form is available, we set it (without adding widget to it) so it can print using the form’s window.

Parameters:



77
78
79
80
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
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 77

def add *items
  items.each do |c|  
    raise ArgumentError, "Nil component passed to add" unless c
    if c.is_a? Widget
      if c.form && c.form != @form
        $log.debug " removing widget VIMSPLIT #{c.class} wr:  #{c.row} row:#{@row} ht:#{@height} "
        c.form.remove_widget c
        c.form = nil
        # or should i just stack them myself and screw what you've asked for
      end
      # take it out of form's control. We will control it.
      if c.form
        c.form.remove_widget c
      end
      # shoot, what if at this point the container does not have a form
      attach_form c if @form
    end
    # most likely if you have created both container and widgets
    # inside app, it would have given row after container

    @components << c
    if c.focusable
      @focusables << c 
      @current_component ||= c # only the first else cursor falls on last on enter
    end

  end # items each
  self
end

#attach_form(c) ⇒ Object

When we get a form, we silently attach it to this object, without the form

knowing. We don't want form managing this object.


109
110
111
112
113
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 109

def attach_form c
  c.form = @form
  c.override_graphic @graphic
  c.parent_component = self
end

#check_component(c) ⇒ Object



163
164
165
166
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 163

def check_component c
  raise "row is less than container #{c.row} #{@row} " if c.row <= @row
  raise "col is less than container #{c.col} #{@col} " if c.col <= @col
end

#correct_component(c) ⇒ Object

correct coordinates of comp esp if App has stacked them after this container It is best to use the simple stack feature. The rest could change at any time

and is quite arbitrary. Some folks may set absolute locations if container
is directly on a form, others may set relative locations if it is inside a 
tabbed pane or other container. Thus, stacks are best


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 125

def correct_component c
  raise "Form is still not set in Container" unless @form
  attach_form(c) unless c.form
  @last_row ||= @row + 1
  inset = 2
  # 2011-10-20 current default behaviour is to stack
  if @positioning == :stack
    c.row = @last_row
    c.col = @col + inset

    # do not advance row, save col for next row
    @last_row += 1
  elsif @positioning == :relative   # UNTESTED NOTE
    if (c.row || 0) <= 0
      $log.warn "c.row in CONTAINER is #{c.row} "
      c.row = @last_row
      @last_row += 1
    elsif c.row > @row + @height -1
      $log.warn "c.row in CONTAINER exceeds container.  #{c.row} "
      c.row -= @height - @row_offset
    else
      # this is where it should come
      c.row += @row + @row_offset
      @last_row = c.row + 1
    end
    if (c.col || 0) <= 0
      c.col = @col + inset + @col_offset
    elsif c.col > @col + @width -1
      c.col -= @width
    elsif c.col == @col
      c.col += @col_offset + inset
    else #f c.col < @col
      c.col += @col+@col_offset
    end
  $log.debug "XXX: CORRECT #{c.name}  r:#{c.row} c:#{c.col} "
  end
  @first_time = false
end

#goto_component(comp) ⇒ Object

set focus on given component Sometimes you have the handle to component, and you want to move focus to it



385
386
387
388
389
390
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 385

def goto_component comp
  return if comp == @current_component
  leave_current_component
  @current_component = comp
  set_form_row
end

#goto_next_componentObject



299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 299

def goto_next_component
  if @current_component != nil 
    leave_current_component
    if on_last_component?
      #@_entered = false
      return :UNHANDLED
    end
    @current_index = @focusables.index(@current_component)
    index = @current_index + 1
    f = @focusables[index]
    if f
      @current_index = index
      @current_component = f
      return set_form_row
    end
  end
  @_entered = false
  return :UNHANDLED
end

#goto_prev_componentObject



318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 318

def goto_prev_component
  if @current_component != nil 
    leave_current_component
    if on_first_component?
      @_entered = false
      return :UNHANDLED
    end
    @current_index = @focusables.index(@current_component)
    index = @current_index -= 1
    f = @focusables[index]
    if f
      @current_index = index
      @current_component = f
      return set_form_row
    end
  end
  return :UNHANDLED
end

#handle_key(ch) ⇒ Object

called by parent or form, otherwise its private



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 211

def handle_key ch
  $log.debug " CONTAINER handle_key #{ch} "
  return if @components.empty?
  _multiplier = ($multiplier == 0 ? 1 : $multiplier )

  # should this go here 2011-10-19 
  unless @_entered
    $log.warn "XXX WARN: calling ON_ENTER since in this situation it was not called"
    on_enter
  end
  if ch == KEY_TAB
    $log.debug "CONTAINER GOTO NEXT TAB"
    return goto_next_component
  elsif ch == KEY_BTAB
    return goto_prev_component
  end
  comp = @current_component
  $log.debug " CONTAINER handle_key #{ch}: #{comp}" 
  if comp
    ret = comp.handle_key(ch) 
    $log.debug " CONTAINER handle_key#{ch}: #{comp} returned #{ret} " 
    if ret != :UNHANDLED
      comp.repaint # NOTE: if we don;t do this, then it won't get repainted. I will have to repaint ALL
      # in repaint of this.
      return ret 
    end
    $log.debug "XXX CONTAINER key unhandled by comp #{comp.name} "
  else
    $log.warn "XXX CONTAINER key unhandled NULL comp"
  end
  case ch
  when ?\C-c.getbyte(0)
    $multiplier = 0
    return 0
  when ?0.getbyte(0)..?9.getbyte(0)
    $log.debug " VIM coming here to set multiplier #{$multiplier} "
    $multiplier *= 10 ; $multiplier += (ch-48)
    return 0
  end
  ret = process_key ch, self
  # allow user to map left and right if he wants
  if ret == :UNHANDLED
    case ch
    when KEY_UP
      # form will pick this up and do needful
      return goto_prev_component #unless on_first_component?
    when KEY_LEFT
      # if i don't check for first component, key will go back to form,
      # but not be processes. so focussed remain here, but be false.
      # In case of returnign an unhandled TAB, on_leave will happen and cursor will move to 
      # previous component outside of this.
      return goto_prev_component unless on_first_component?
    when KEY_RIGHT
      return goto_next_component #unless on_last_component?
    when KEY_DOWN
      return goto_next_component #unless on_last_component?
    else 
      @_entered = false
      return :UNHANDLED
    end
  end

  $multiplier = 0
  return 0
end

#init_varsObject



61
62
63
64
65
66
67
68
69
70
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 61

def init_vars
  @repaint_required = true
  @row_offset = @col_offset = 0 if @suppress_borders # FIXME supposed to use this !!

  @internal_width = 2
  @internal_width = 1 if @suppress_borders
  @name ||= "AContainer"
  @first_time = true

end

#leave_current_componentObject

leave the component we are on. This should be followed by all containers, so that the on_leave action of earlier comp can be displayed, such as dimming components selections



365
366
367
368
369
370
371
372
373
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 365

def leave_current_component
  @current_component.on_leave
  # NOTE this is required, since repaint will just not happen otherwise
  # Some components are erroneously repainting all, after setting this to true so it is 
  # working there. 
  @current_component.repaint_required true
  $log.debug " after on_leave RCONT XXX #{@current_component.focussed}   #{@current_component.name}"
  @current_component.repaint
end

#on_enterObject

Actually we should only go to current component if it accepted a key stroke. if user tabbed thru it, then no point going back to it. Go to first or last depending on TAB or BACKTAB otherwise. NOTE: if user comes in using DOWN or UP, last traversed component will get the focus



281
282
283
284
285
286
287
288
289
290
291
292
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 281

def on_enter
  # if BTAB, the last comp XXX they must be focusable FIXME
  if $current_key == KEY_BTAB || $current_key == KEY_UP
    @current_component = @focusables.last
  else
    @current_component = @focusables.first
  end
  return unless @current_component
  $log.debug " CONTAINER came to ON_ENTER #{@current_component} "
  set_form_row
  @_entered = true
end

#on_first_component?Boolean

is focus on first component FIXME check for focusable

Returns:

  • (Boolean)


376
377
378
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 376

def on_first_component?
  @current_component == @focusables.first
end

#on_last_component?Boolean

is focus on last component FIXME check for focusable

Returns:

  • (Boolean)


380
381
382
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 380

def on_last_component?
  @current_component == @focusables.last
end

#on_leaveObject

we cannot be sure that this will be called especially if this is embedded inside some other component



295
296
297
298
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 295

def on_leave
  @_entered = false
  super
end

#repaintObject

repaint object called by Form, and sometimes parent component (if not form).



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 171

def repaint
  my_win = @form ? @form.window : @target_window
  @graphic = my_win unless @graphic
  raise " #{@name} NO GRAPHIC set as yet                 CONTAINER paint " unless @graphic
  @components.each { |e| correct_component e } if @first_time
  #@components.each { |e| check_component e } # seeme one if printing out

  #return unless @repaint_required

  # if some major change has happened then repaint everything
  if @repaint_required
    $log.debug " VIM repaint graphic #{@graphic} "
    print_borders unless @suppress_borders # do this once only, unless everything changes
    @components.each { |e| e.repaint_all(true); e.repaint }
  else
    @components.each { |e| e.repaint }
  end # if repaint_required

  @repaint_required = false
end

#set_form_colObject



356
357
358
359
360
361
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 356

def set_form_col
  return if @current_component.nil?
  $log.debug " #{@name} CONTAINER EMPTY set_form_col calling sfc for #{@current_component.name} "
  # already called from above.
  #@current_component.set_form_col 
end

#set_form_rowObject

private XXX why are we calling 3 methods in a row, why not OE manages these 3 There’s double calling going on.



339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 339

def set_form_row
  return :UNHANDLED if @current_component.nil?
  cc = @current_component
  $log.debug "CONT #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
  $log.debug " CONTAINER on enter sfr #{@current_component.name}  #{@current_component} "

  # bug caught here. we were printing a field before it had been set, so it printed out
  @components.each { |e| correct_component e } if @first_time
  @current_component.on_enter
  @current_component.set_form_row # why was this missing in vimsplit. is it
    $log.debug "CONT2 #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
  # that on_enter does a set_form_row
  @current_component.set_form_col # XXX 
  @current_component.repaint # OMG this could happen before we've set row and col
  # XXX compo should do set_form_row and col if it has that
end

#widgetsObject



115
# File 'lib/rbcurse/core/widgets/rcontainer.rb', line 115

def widgets; @components; end