ncumbra 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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