ncumbra 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/umbra/form.rb CHANGED
@@ -10,6 +10,11 @@ require 'umbra/keymappinghandler' # for bind_key and process_key
10
10
  # has bound the key via +bind_key+.
11
11
  # NOTE : 2018-03-08 - now using @focusables instead of @widgets in traversal.
12
12
  # active_index is now index into focusables.
13
+ ## Events: RESIZE (allows listener to reposition objects that have variable widths or heights)
14
+ ## NOTE: active_index: 2018-05-17 - is being set to 0 even though no field is active. Thus, first time
15
+ # on_enter does not fire. It should only be set after a field is focused and its on_enter has succeeded.
16
+ #
17
+
13
18
  module Umbra
14
19
  class Form
15
20
  # array of widgets, and those that can be traversed
@@ -69,9 +74,9 @@ class Form
69
74
  end
70
75
  # maintain a list of focusable objects so form can traverse between them easily.
71
76
  def update_focusables
72
- $log.debug "1 inside update_focusables #{@focusables.count} "
77
+ #$log.debug "1 inside update_focusables #{@focusables.count} "
73
78
  @focusables = @widgets.select { |w| w.focusable }
74
- $log.debug "2 inside update_focusables #{@focusables.count} "
79
+ #$log.debug "2 inside update_focusables #{@focusables.count} "
75
80
  end
76
81
  # Decide layout of objects. User has to call this after creating components
77
82
  # More may come here.
@@ -108,6 +113,42 @@ class Form
108
113
  self
109
114
  end
110
115
 
116
+ ## set focussed widget to given fld.
117
+ ## This ensures that whenever a widget is given focus, the on_leave of the previous widget
118
+ ## is called, and the on_enter of this field is called.
119
+ ## 2018-05-18 - rewrite of select_field which did not call on_leave
120
+ def focussed_widget fld
121
+
122
+ return nil unless fld ## no focusable
123
+
124
+ if fld.is_a? Integer
125
+ fld = @focusables[fld]
126
+ end
127
+
128
+ return unless fld.focusable
129
+
130
+ ## leave existing widget if there was one
131
+ fw = @_focussed_widget
132
+ return if fw == fld
133
+
134
+ if fw
135
+ on_leave fw
136
+ end
137
+
138
+
139
+ ## enter given widget
140
+ on_enter fld
141
+ @_focussed_widget = fld
142
+ ix = @focusables.index fld
143
+ @active_index = ix
144
+
145
+ @row, @col = fld.rowcol
146
+ _setrowcol @row, @col
147
+ repaint # 2018-03-21 - handle_key calls repaint, is this for cases not involving keypress ?
148
+ @window.refresh
149
+ end
150
+ alias :select_field :focussed_widget
151
+
111
152
 
112
153
  # form repaint,calls repaint on each widget which will repaint it only if it has been modified since last call.
113
154
  # called after each keypress and on select_field.
@@ -122,6 +163,7 @@ class Form
122
163
  f.graphic = @window unless f.graphic # messageboxes may not have a window till very late
123
164
  f.repaint
124
165
  f.repaint_required = false
166
+ f.instance_variable_set(:@_object_created, true) ## after this property_change handlers will fire
125
167
  end
126
168
  end
127
169
 
@@ -134,10 +176,23 @@ class Form
134
176
  @window.wrefresh
135
177
  end
136
178
  # @return [Widget, nil] current field, nil if no focusable field
179
+ ## NOTE 2018-05-17 - this is called by form in the very beginning when no field is actually focussed
180
+ ## but active_index has been set to 0, so the on_enter has not been executed, but the handle_key
181
+ ## is invoked.
137
182
  def get_current_field
183
+ =begin
138
184
  #select_next_field if @active_index == -1
139
185
  return nil if @active_index.nil? # for forms that have no focusable field 2009-01-08 12:22
140
186
  @focusables[@active_index]
187
+ =end
188
+
189
+
190
+ ## rewrite on 2018-05-18 - so that on_enter is called for first field
191
+ if @_focussed_widget.nil? ## when form handle_key first called
192
+ focussed_widget @focusables.first
193
+ end
194
+ return @_focussed_widget
195
+
141
196
  end
142
197
  alias :current_widget :get_current_field
143
198
  # take focus to first focusable field
@@ -162,7 +217,7 @@ class Form
162
217
  # puts focus on the given field/widget index
163
218
  # @param index of field in @widgets (or can be a Widget too)
164
219
  # XXX if called externally will not run a on_leave of previous field
165
- def select_field ix0
220
+ def OLDselect_field ix0
166
221
  if ix0.is_a? Widget
167
222
  ix0 = @focusables.index(ix0)
168
223
  end
@@ -183,41 +238,27 @@ class Form
183
238
  $log.debug "inside select field ENABLED FALSE : act #{@active_index} ix0 #{ix0}"
184
239
  end
185
240
  end
186
- ##
187
- # run validate_field on a field, usually whatevers current
188
- # before transferring control
189
- # We should try to automate this so developer does not have to remember to call it.
190
- # # @param field object
191
- # @return [0, -1] for success or failure
192
- # NOTE : catches exception and sets $error_message, check if -1
193
- def validate_field f=@focusables[@active_index]
194
- begin
195
- on_leave f
196
- rescue => err
197
- $log.error "form: validate_field caught EXCEPTION #{err}"
198
- $log.error(err.backtrace.join("\n"))
199
- # $error_message = "#{err}" # changed 2010
200
- #$error_message.value = "#{err}" # 2018-03-18 - commented off since no Variable any longer
201
- FFI::NCurses.beep
202
- return -1
203
- end
204
- return 0
205
- end
241
+
206
242
  # put focus on next field
207
243
  # will cycle by default, unless navigation policy not :CYCLICAL
208
244
  # in which case returns :NO_NEXT_FIELD.
209
- # FIXME: in the beginning it comes in as -1 and does an on_leave of last field
210
245
  # 2018-03-07 - UMBRA: let us force user to run validation when he does next field
211
246
  def select_next_field
212
247
  return :UNHANDLED if @focusables.nil? || @focusables.empty?
213
- #$log.debug "insdie sele nxt field : #{@active_index} WL:#{@widgets.length}"
248
+ index = nil
249
+ if @_focussed_widget
250
+ index = @focusables.index @_focussed_widget
251
+ end
252
+ index = index ? index+1 : 0
253
+ index = 0 if index >= @focusables.length # CYCLICAL 2018-03-11 -
254
+ focussed_widget @focusables[index]
255
+ =begin
256
+ return :UNHANDLED if @focusables.nil? || @focusables.empty?
214
257
  if @active_index.nil? || @active_index == -1 # needs to be tested out A LOT
215
- # what is this silly hack for still here 2014-04-24 - 13:04 DELETE FIXME
216
- @active_index = -1
217
- @active_index = 0 # 2018-03-08 - NOT_SURE
258
+ @active_index = 0
218
259
  end
219
260
  f = @focusables[@active_index]
220
- # we need to call on_leave of this field or else state will never change back to normal TODO
261
+ # we need to call on_leave of this field or else state will never change back to normal
221
262
  on_leave f
222
263
  #index = @focusables.index(f)
223
264
  index = @active_index
@@ -232,6 +273,7 @@ class Form
232
273
  #
233
274
  $log.debug "inside sele nxt field : NO NEXT #{@active_index} WL:#{@widgets.length}"
234
275
  return :NO_NEXT_FIELD
276
+ =end
235
277
  end
236
278
  ##
237
279
  # put focus on previous field
@@ -240,6 +282,17 @@ class Form
240
282
  # @return [nil, :NO_PREV_FIELD] nil if cyclical and it finds a field
241
283
  # if not cyclical, and no more fields then :NO_PREV_FIELD
242
284
  def select_prev_field
285
+ return :UNHANDLED if @focusables.nil? or @focusables.empty?
286
+ index = nil
287
+ if @_focussed_widget
288
+ index = @focusables.index @_focussed_widget
289
+ else
290
+ index = @focusables.length
291
+ end
292
+ index -= 1
293
+ index = @focusables.length-1 if index < 0 # CYCLICAL 2018-03-11 -
294
+ focussed_widget @focusables[index]
295
+ =begin
243
296
  return :UNHANDLED if @focusables.nil? or @focusables.empty?
244
297
  #$log.debug "insdie sele prev field : #{@active_index} WL:#{@widgets.length}"
245
298
  if @active_index.nil?
@@ -258,6 +311,7 @@ class Form
258
311
  end
259
312
 
260
313
  return :NO_PREV_FIELD
314
+ =end
261
315
  end
262
316
 
263
317
  private
@@ -312,7 +366,7 @@ class Form
312
366
  f.on_enter if f.respond_to? :on_enter
313
367
  end
314
368
 
315
- def _process_key keycode, object, window
369
+ def OLD_process_key keycode, object, window
316
370
  return :UNHANDLED if @_key_map.nil?
317
371
  blk = @_key_map[keycode]
318
372
  $log.debug "XXX: _process key keycode #{keycode} #{blk.class}, #{self.class} "
@@ -338,9 +392,9 @@ class Form
338
392
  # returns UNHANDLED if no block for it
339
393
  # after form handles basic keys, it gives unhandled key to current field, if current field returns
340
394
  # unhandled, then it checks this map.
341
- # Please update widget with any changes here. TODO: match regexes as in mapper
395
+ # Please update widget with any changes here.
342
396
 
343
- def process_key keycode, object
397
+ def OLDprocess_key keycode, object # already there in keymappinghandler
344
398
  return _process_key keycode, object, @window
345
399
  end # }}}
346
400
 
@@ -355,7 +409,6 @@ class Form
355
409
  @keys_mapped = true
356
410
  end
357
411
 
358
- =begin
359
412
  # repaint all # {{{
360
413
  # this forces a repaint of all visible widgets and has been added for the case of overlapping
361
414
  # windows, since a black rectangle is often left when a window is destroyed. This is internally
@@ -364,20 +417,21 @@ class Form
364
417
  # the window itself may need recreating ? 2014-08-18 - 21:03
365
418
  def repaint_all_widgets
366
419
  $log.debug " REPAINT ALL in FORM called "
367
- raise "it has come to repaint_all"
420
+ #raise "it has come to repaint_all"
368
421
  @widgets.each do |w|
369
422
  next if w.visible == false
370
423
  #next if w.class.to_s == "Canis::MenuBar"
371
424
  $log.debug " ---- REPAINT ALL #{w.name} "
372
- #w.repaint_required true
373
- w.repaint_all true
425
+ w.repaint_required = true
426
+ #w.repaint_all true
374
427
  w.repaint
375
428
  end
376
429
  $log.debug " REPAINT ALL in FORM complete "
377
430
  # place cursor on current_widget
378
431
  _setpos
379
432
  end # }}}
380
- =end
433
+
434
+
381
435
  ## forms handle keys {{{
382
436
  # mainly traps tab and backtab to navigate between widgets.
383
437
  # I know some widgets will want to use tab, e.g edit boxes for entering a tab
@@ -392,42 +446,52 @@ class Form
392
446
  when -1
393
447
  #repaint # only for continuous updates, and will need to use wtimeout and not nodelay in getch
394
448
  return
395
- =begin
396
- when 1000, 12
449
+ when 1000, 18 # what if someone has trapped this.
397
450
  # NOTE this works if widgets cover entire screen like text areas and lists but not in
398
451
  # dialogs where there is blank space. only widgets are painted.
399
- # testing out 12 is C-l
452
+ # testing out 12 is C-l, 18 is C-r
400
453
  $log.debug " form REFRESH_ALL repaint_all HK #{ch} #{self}, #{@name} "
454
+ FFI::NCurses.endwin
455
+ @window.wrefresh ## endwin must be followed by refresh
456
+ @window.repaint
401
457
  repaint_all_widgets
402
- return
458
+ @window.wrefresh
459
+ return 0
403
460
  when FFI::NCurses::KEY_RESIZE # SIGWINCH # UNTESTED XXX
461
+ ## NOTE: this works but boxes are not resized since hardcoded height and width were given.
462
+ ## 2018-05-13 - only if a layout is used, can a recalc happen.
404
463
  # note that in windows that have dialogs or text painted on window such as title or
405
464
  # box, the clear call will clear it out. these are not redrawn.
406
- lines = FFI::NCurses.LINES
407
- cols = FFI::NCurses.COLS
408
- x = FFI::NCurses.stdscr.getmaxy
409
- y = FFI::NCurses.stdscr.getmaxx
410
- $log.debug " form RESIZE HK #{ch} #{self}, #{@name}, #{ch}, x #{x} y #{y} lines #{lines} , cols: #{cols} "
411
- #alert "SIGWINCH WE NEED TO RECALC AND REPAINT resize #{lines}, #{cols}: #{x}, #{y} "
412
-
465
+
413
466
  # next line may be causing flicker, can we do without.
414
467
  FFI::NCurses.endwin
415
468
  @window.wrefresh
416
469
  @window.wclear
417
- if @layout_manager
418
- @layout_manager.do_layout
419
- # we need to redo statusline and others that layout ignores
420
- else
421
- @widgets.each { |e| e.repaint_all(true) } # trying out
422
- end
470
+ lines = FFI::NCurses.LINES
471
+ cols = FFI::NCurses.COLS
472
+ #x = FFI::NCurses.stdscr.getmaxy
473
+ x = @window.getmaxy
474
+ #y = FFI::NCurses.stdscr.getmaxx
475
+ y = @window.getmaxx
476
+ @window.wresize(x,y)
477
+ $log.debug " form RESIZE SIGWINCH HK #{ch} #{self}, #{@name}, #{ch}, x #{x} y #{y} lines #{lines} , cols: #{cols} "
478
+ #alert "SIGWINCH WE NEED TO RECALC AND REPAINT resize #{lines}, #{cols}: #{x}, #{y} "
479
+
480
+ repaint_all_widgets
481
+ #if @layout_manager
482
+ #@layout_manager.do_layout
483
+ ## we need to redo statusline and others that layout ignores
484
+ #else
485
+ #end
423
486
  ## added RESIZE on 2012-01-5
424
487
  ## stuff that relies on last line such as statusline dock etc will need to be redrawn.
425
488
  fire_handler :RESIZE, self
426
- =end
489
+ @window.wrefresh
427
490
  else
428
491
  field = get_current_field
429
492
  handled = :UNHANDLED
430
493
  handled = field.handle_key ch unless field.nil? # no field focussable
494
+ ## next line "field" can print entire content of a list or table if to_s is large
431
495
  $log.debug "handled inside Form #{ch} from #{field} got #{handled} "
432
496
  # some widgets like textarea and list handle up and down
433
497
  if handled == :UNHANDLED or handled == -1 or field.nil?
@@ -4,7 +4,7 @@
4
4
  # Author: j kepler http://github.com/mare-imbrium/canis/
5
5
  # Date: 2018-04-05 - 08:34
6
6
  # License: MIT
7
- # Last update: 2018-04-17 08:59
7
+ # Last update: 2018-04-22 23:01
8
8
  # ----------------------------------------------------------------------------- #
9
9
  # keymappinghandler.rb Copyright (C) 2018 j kepler
10
10
 
@@ -74,7 +74,7 @@ module Umbra
74
74
  def _process_key keycode, object, window
75
75
  return :UNHANDLED if @_key_map.nil?
76
76
  blk = @_key_map[keycode]
77
- $log.debug "XXX: _process key keycode #{keycode} #{blk.class}, #{self.class} "
77
+ #$log.debug "XXX: _process key keycode #{keycode} #{blk.class}, #{self.class} "
78
78
  return :UNHANDLED if blk.nil?
79
79
 
80
80
  if blk.is_a? Symbol
@@ -82,12 +82,12 @@ module Umbra
82
82
  return send(blk, *@_key_args[keycode])
83
83
  else
84
84
  ## 2013-03-05 - 19:50 why the hell is there an alert here, nowhere else
85
- $log.error "This ( #{self.class} ) does not respond to #{blk.to_s} [PROCESS-KEY]"
85
+ $log.error "This ( #{self.class} ) does not respond to #{blk.to_s} [PROCESS-KEY]" if $log
86
86
  # added 2013-03-05 - 19:50 so called can know
87
87
  return :UNHANDLED
88
88
  end
89
89
  else
90
- $log.debug "rwidget BLOCK called _process_key " if $log.debug?
90
+ #$log.debug "rwidget BLOCK called _process_key " if $log.debug?
91
91
  return blk.call object, *@_key_args[keycode]
92
92
  end
93
93
  end
data/lib/umbra/label.rb CHANGED
@@ -5,7 +5,7 @@
5
5
  # Author: j kepler http://github.com/mare-imbrium/canis/
6
6
  # Date: 2018-03-08 - 14:04
7
7
  # License: MIT
8
- # Last update: 2018-04-21 23:55
8
+ # Last update: 2018-05-22 23:36
9
9
  # ----------------------------------------------------------------------------- #
10
10
  # label.rb Copyright (C) 2018- j kepler
11
11
  #
@@ -16,7 +16,7 @@ module Umbra
16
16
  class Label < Widget
17
17
 
18
18
  # justify required a display length, esp if center.
19
- attr_accessor :justify #:right, :left, :center
19
+ attr_property :justify #:right, :left, :center
20
20
  attr_accessor :mnemonic # alt-key that passes focus to related field
21
21
  attr_accessor :related_widget # field related to this label. See +mnemonic+.
22
22
 
@@ -45,7 +45,9 @@ class Label < Widget
45
45
  def repaint
46
46
  return unless @repaint_required
47
47
  raise "Label row or col is nil #{@row} , #{@col}, #{@text} " if @row.nil? || @col.nil?
48
- r,c = rowcol
48
+ #r,c = rowcol
49
+ r = self.row
50
+ c = self.col
49
51
  $log.debug "label repaint #{r} #{c} #{@text} "
50
52
 
51
53
  # value often nil so putting blank, but usually some application error
@@ -55,29 +57,40 @@ class Label < Widget
55
57
  value = value.join " "
56
58
  end
57
59
  # ensure we do not exceed
58
- if @width
59
- if value.length > @width
60
- value = value[0..@width-1]
61
- end
62
- end
63
- len = @width || value.length
60
+ # ## TODO do this in the format commented on 2018-05-22 -
61
+ _width = self.width
62
+ #if _width
63
+ #if value.length > _width
64
+ #value = value[0.._width-1]
65
+ #end
66
+ #end
67
+ len = _width || value.length
64
68
  acolor = @color_pair || 0
65
- str = @justify.to_sym == :right ? "%*s" : "%-*s" # added 2008-12-22 19:05
69
+ #str = @justify.to_sym == :right ? "%*s" : "%-*s" # added 2008-12-22 19:05
70
+ str = @justify.to_sym == :right ? "%#{len}.#{len}s" : "%-#{len}.#{len}s" # added 2008-12-22 19:05
66
71
 
67
- #@graphic ||= @form.window
68
72
  # clear the area
69
73
  @graphic.printstring r, c, " " * len , acolor, @attr
70
74
  if @justify.to_sym == :center
71
- padding = (@width - value.length)/2
75
+ padding = (_width - value.length)/2
72
76
  value = " "*padding + value + " "*padding # so its cleared if we change it midway
73
77
  end
74
- @graphic.printstring r, c, str % [len, value], acolor, @attr
78
+ ## move this into paint_label or something so we can override.
79
+ # try a block which was passed earlier which gets a string TODO
80
+ #@graphic.printstring r, c, str % [value], acolor, @attr
81
+ print_label @graphic, r, c, str , value, acolor, @attr
75
82
  if @mnemonic
76
83
  ulindex = value.index(@mnemonic) || value.index(@mnemonic.swapcase)
77
84
  @graphic.mvchgat(y=r, x=c+ulindex, max=1, BOLD|UNDERLINE, acolor, nil)
78
85
  end
79
86
  @repaint_required = false
80
87
  end
88
+
89
+ ## The mwthod that finally prints the label text.
90
+ ## Override this to do any customised printing such as multiple colors.
91
+ def print_label(win, row, col, format, value, _color, _attr)
92
+ win.printstring row, col, format % [value], _color, _attr
93
+ end
81
94
  # Added 2011-10-22 to prevent some naive components from putting focus here.
82
95
  def on_enter
83
96
  raise "Cannot enter Label"
@@ -86,10 +99,11 @@ class Label < Widget
86
99
  raise "Cannot leave Label"
87
100
  end
88
101
  # overriding so that label is redrawn, since this is the main property that is used.
89
- def text=(_text)
90
- @text = _text
91
- self.touch
92
- end
102
+ ## 2018-05-21 - we are now using attr_property so not required
103
+ #def text=(_text)
104
+ #@text = _text
105
+ #self.touch
106
+ #end
93
107
  # ADD HERE LABEL
94
108
  end # }}}
95
109
  end # module
@@ -4,12 +4,11 @@
4
4
  # Author: j kepler http://github.com/mare-imbrium/canis/
5
5
  # Date: 2018-04-12 - 23:35
6
6
  # License: MIT
7
- # Last update: 2018-04-21 23:58
7
+ # Last update: 2018-05-14 14:34
8
8
  # ----------------------------------------------------------------------------- #
9
9
  # labeledfield.rb Copyright (C) 2018 j kepler
10
10
  require 'umbra/field'
11
11
  module Umbra
12
- # TODO should be able to add a mnemonic here for the label since the association exists
13
12
  # TODO we should consider creating a Label, so user can have more control. Or allow user
14
13
  # to supply a Label i/o a String ???
15
14
  #
@@ -30,15 +29,15 @@ module Umbra
30
29
  # It is initialized exactly like a Field, with the addition of label (and optionally label_color_pair,
31
30
  # label_attr, and lcol, lrow)
32
31
  #
33
- attr_accessor :label # label of field, just a String
32
+ attr_property :label # label of field, just a String
34
33
  # if lrow and lcol are specified then label is printed exactly at that spot.
35
34
  # If they are omitted, then label is printed on left of field. Omit the lcol if you want
36
35
  # the fields to be aligned, one under another, with the labels right-aligned.
37
- attr_accessor :lrow, :lcol # coordinates of the label
38
- attr_accessor :label_color_pair # label of field color_pair
39
- attr_accessor :label_attr # label of field attribute
40
- attr_accessor :label_highlight_color_pair # label of field high color_pair
41
- attr_accessor :label_highlight_attr # label of field high attribute
36
+ attr_property :lrow, :lcol # coordinates of the label
37
+ attr_property :label_color_pair # label of field color_pair
38
+ attr_property :label_attr # label of field attribute
39
+ attr_property :label_highlight_color_pair # label of field high color_pair
40
+ attr_property :label_highlight_attr # label of field high attribute
42
41
  attr_accessor :mnemonic # mnemonic of field which shows up on label
43
42
  attr_accessor :related_widget # to keep sync with label
44
43
  def initialize config={}, &block