Class: RETerm::Window

Inherits:
Object
  • Object
show all
Includes:
EventDispatcher, LogHelpers
Defined in:
lib/reterm/window.rb

Overview

Windows are areas rendered on screen, associated with components to be rendered in them. They specify the position to start drawing component as well as the maximum width and height. A border may be drawn around a window and a ColorPair associated.

If Layout is added to a Window, children may subsequently be added. This should be performed via the Layout#add_child method.

Examples:

adding a layout to a window

init_reterm {
  win = Window.new :rows => 50, :cols => 30
  layout = Layouts::Horizontal.new
  win.component = layout

  child = layout.add_child :rows => 5, :cols => 10
  child.class # => RETerm::Window

  label = Components::Label.new :text => "Hello World!"
  child.component = label

  update_reterm
  sleep(5)
}

Constant Summary

Constants included from LogHelpers

LogHelpers::LOG_FILE

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from LogHelpers

#logger

Methods included from EventDispatcher

#dispatch, #handle

Constructor Details

#initialize(args = {}) ⇒ Window

Instantiate Window with given args. None are required, but unless :rows, :cols, :x, or :y is specified, window will be created in it’s default position.

This method will generate a unique id for each window and add it to a static registry for subsequent tracking.

Parameters:

  • args (Hash) (defaults to: {})

    arguments used to instantiate window

Options Hash (args):

  • :rows (Integer)

    number of rows to assign to window

  • :cols (Integer)

    number of cols to assign to window

  • :x (Integer)

    starting x position of window

  • :y (Integer)

    starting y position of window

  • :vborder (Integer)

    vertical border char

  • :hborder (Integer)

    horizontal border char

  • :component (Component)

    component to assign to window

  • :parent (Window)

    parent to assign to window, if set window will be created a a child, else it will be independently created & tracked.

Raises:

  • (ArgumentError)

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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/reterm/window.rb', line 130

def initialize(args={})
  @@registry ||= []
  @@registry  << self

  @@wid ||= 0
  @@wid  += 1
  @window_id = @@wid

  @children = []

  @x    = args[:x] || 0
  @y    = args[:y] || 0

  @vborder = args[:vborder] || 0
  @hborder = args[:hborder] || 0

  component = args[:component]

  @rows = args[:rows] ||
          (component ?
           (component.requested_rows + component.extra_padding) :
           Terminal.rows)

  @cols = args[:cols] ||
          (component ?
           (component.requested_cols + component.extra_padding) :
           Terminal.cols)

  if args[:parent]
    @parent = args[:parent]

    @rows, @cols = *Window.adjust_proportional(@parent, @rows, @cols)
    @x,    @y    = *Window.align(@parent, @x, @y, @rows, @cols)

    @win = parent.win.derwin(@rows, @cols, @y, @x)

  else
    @parent = nil

    @rows, @cols = *Window.adjust_proportional(Terminal, @rows, @cols)
    @x,    @y    = *Window.align(Terminal, @x, @y, @rows, @cols)

    @win = Ncurses::WINDOW.new(@rows, @cols, @y, @x)
  end

  raise ArgumentError, "could not create window" if !@win

  self.component = component if !!component

  Ncurses::keypad(@win, true)

  @win.timeout(SYNC_TIMEOUT) if sync_enabled? # XXX

  @expand      = !!args[:expand]
  @must_expand = !!args[:must_expand]

  @fill        = !!args[:fill]
end

Instance Attribute Details

#childrenObject

Returns the value of attribute children.


43
44
45
# File 'lib/reterm/window.rb', line 43

def children
  @children
end

#colsObject

Returns the value of attribute cols.


32
33
34
# File 'lib/reterm/window.rb', line 32

def cols
  @cols
end

#componentObject

Returns the value of attribute component.


38
39
40
# File 'lib/reterm/window.rb', line 38

def component
  @component
end

#hborderObject

Returns the value of attribute hborder.


36
37
38
# File 'lib/reterm/window.rb', line 36

def hborder
  @hborder
end

#parentObject (readonly)

Returns the value of attribute parent.


42
43
44
# File 'lib/reterm/window.rb', line 42

def parent
  @parent
end

#rowsObject

Returns the value of attribute rows.


32
33
34
# File 'lib/reterm/window.rb', line 32

def rows
  @rows
end

#vborderObject

Returns the value of attribute vborder.


35
36
37
# File 'lib/reterm/window.rb', line 35

def vborder
  @vborder
end

#winObject (readonly)

Returns the value of attribute win.


40
41
42
# File 'lib/reterm/window.rb', line 40

def win
  @win
end

#window_idObject (readonly)

Returns the value of attribute window_id.


30
31
32
# File 'lib/reterm/window.rb', line 30

def window_id
  @window_id
end

#xObject

Returns the value of attribute x.


33
34
35
# File 'lib/reterm/window.rb', line 33

def x
  @x
end

#yObject

Returns the value of attribute y.


33
34
35
# File 'lib/reterm/window.rb', line 33

def y
  @y
end

Class Method Details

.adjust_proportional(parent, rows, cols) ⇒ Object

Adjusts rows/cols in a context dependent manner TODO should proporational percentage be of remaining area?


193
194
195
196
197
198
199
200
201
# File 'lib/reterm/window.rb', line 193

def self.adjust_proportional(parent, rows, cols)
  nr = rows
  nc = cols

  nr = parent.rows * nr if rows < 1
  nc = parent.cols * nc if cols < 1

  return nr, nc
end

.align(parent, x, y, rows, cols) ⇒ Object

Adjusts x/y in context dependent manner


204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/reterm/window.rb', line 204

def self.align(parent, x, y, rows, cols)
  nx = x
  ny = y

  nx = 1 if x == :left
  ny = 1 if y == :top

  nx = parent.cols - cols - 1 if x == :right
  ny = parent.rows - rows - 1 if y == :bottom

  nx = parent.cols / 2 - cols / 2 if x == :center
  ny = parent.rows / 2 - rows / 2 if y == :center

  return nx, ny
end

.allObject

Static method returning all tracked windows


303
304
305
306
# File 'lib/reterm/window.rb', line 303

def self.all
  @@registry ||= []
  @@registry
end

.fill_parent(parent, x, y, rows, cols) ⇒ Object

Adjusts rows / cols so as to fill parent


221
222
223
224
225
226
227
228
229
230
231
# File 'lib/reterm/window.rb', line 221

def self.fill_parent(parent, x, y, rows, cols)
  if (y + rows) < parent.rows
    rows = parent.rows - y - 1
  end

  if (x + cols) < parent.cols
    cols = parent.cols - x - 1
  end

  return rows, cols
end

.topObject

Static method returning top level windows


309
310
311
312
# File 'lib/reterm/window.rb', line 309

def self.top
  @@registry ||= []
  @@registry.select { |w| !w.parent? }
end

Instance Method Details

#activate!(*input) ⇒ Object

Activate window component


530
531
532
# File 'lib/reterm/window.rb', line 530

def activate!(*input)
  component.activate!(*input)
end

#actual_colsObject

Return window cols


520
521
522
# File 'lib/reterm/window.rb', line 520

def actual_cols
  dimensions[1]
end

#actual_rowsObject

Return window rows


515
516
517
# File 'lib/reterm/window.rb', line 515

def actual_rows
  dimensions[0]
end

#bold!Object

Enable bold formatting


460
461
462
463
464
465
466
# File 'lib/reterm/window.rb', line 460

def bold!
  @win.attron(Ncurses::A_BOLD)
  return unless block_given?

  yield
  @win.attroff(Ncurses::A_BOLD)
end

#border!Object

Draw Border around window


423
424
425
# File 'lib/reterm/window.rb', line 423

def border!
  @win.box(@vborder, @hborder)
end

#cdk?Boolean

Return bool indicating if cdk is enabled for this window/component

Returns:

  • (Boolean)

296
297
298
# File 'lib/reterm/window.rb', line 296

def cdk?
  !!@cdk_scr
end

#cdk_scrObject

Return cdk screen (only used by CDK components)


290
291
292
293
# File 'lib/reterm/window.rb', line 290

def cdk_scr
  enable_cdk!
  @cdk_scr ||= CDK::SCREEN.new(@win)
end

#child_containing(x, y, z) ⇒ Object

Return child containing specified screen coordiantes, else nil


336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/reterm/window.rb', line 336

def child_containing(x, y, z)
  found = nil
  children.find { |c|
    next if found

    # recursively descend into layout children
    if c.component.kind_of?(Layout)
      found = c.child_containing(x, y, z)

    else
      found =
        c.total_x <= x && c.total_y <= y && # c.z >= z
       (c.total_x + c.cols) >= x && (c.total_y + c.rows) >= y
      found = c if found
    end
  }

  found
end

#children?Boolean

Return boolean indicating if this window has children

Returns:

  • (Boolean)

67
68
69
# File 'lib/reterm/window.rb', line 67

def children?
  !@children.empty?
end

#clear!Object

Clear window by removing all children and reinitializing window space


371
372
373
374
375
376
377
378
# File 'lib/reterm/window.rb', line 371

def clear!
  children.each { |c|
    del_child(c)
  }

  @children = []
  erase
end

#colored?Boolean

Return bool indiciating if colors are set

Returns:

  • (Boolean)

488
489
490
# File 'lib/reterm/window.rb', line 488

def colored?
  !!@colors
end

#colors=(c) ⇒ Object

Set window color


493
494
495
496
497
498
499
500
501
502
# File 'lib/reterm/window.rb', line 493

def colors=(c)
  @colors = c.is_a?(ColorPair) ? c : ColorPair.for(c).first
  @win.bkgd(Ncurses.COLOR_PAIR(@colors.id)) unless @win.nil?

  component.colors = @colors if component?

  children.each { |ch|
    ch.colors = c
  }
end

#component?Boolean

Return bool indicating if this window has a component associated with it

Returns:

  • (Boolean)

47
48
49
# File 'lib/reterm/window.rb', line 47

def component?
  !!@component
end

#create_child(h = {}) ⇒ Object

Create child window, this method should not be invoked by end-user, rather it is is invoked the Layout#add_child is called.


319
320
321
322
323
324
# File 'lib/reterm/window.rb', line 319

def create_child(h={})
  c = self.class.new h.merge(:parent => self)
  c.colors = @colors if colored?
  children << c
  c
end

#del_child(child) ⇒ Object

Remove child window, like #create_child, this is used internally and should not be invoked by the end user


328
329
330
331
332
333
# File 'lib/reterm/window.rb', line 328

def del_child(child)
  @children.delete(child)
  @@registry.delete(child)
  child.finalize!
  child.win.delwin if !!child.win
end

#dimensionsObject

Return window dimensions as an array containing rows & cols


505
506
507
508
509
510
511
512
# File 'lib/reterm/window.rb', line 505

def dimensions
  rows = []
  cols = []
  @win.getmaxyx(rows, cols)
  rows = rows.first
  cols = cols.first
  [rows, cols]
end

#distance_from(win) ⇒ Object


98
99
100
101
102
# File 'lib/reterm/window.rb', line 98

def distance_from(win)
  # FIXME: need to expand to compare each of 4 window coords
  #        to each of 4 in other window
  Math.sqrt((x - win.x) ** 2 + (y - win.y) ** 2)
end

#draw!Object

Draw component in window


525
526
527
# File 'lib/reterm/window.rb', line 525

def draw!
  component.draw! if component?
end

#eraseObject

Erase window drawing area


381
382
383
384
385
386
387
388
389
# File 'lib/reterm/window.rb', line 381

def erase
  children.each { |c|
    c.erase
  }

  @win.werase if @win

  component.component.erase if cdk? && component? && component.init?
end

#erase_scrObject

Erases window screen by overwriting it with blanks


392
393
394
395
396
# File 'lib/reterm/window.rb', line 392

def erase_scr
  0.upto(rows) { |r|
    mvaddstr(r, 1, " " * cols)
  }
end

#expand?Boolean

Returns:

  • (Boolean)

251
252
253
# File 'lib/reterm/window.rb', line 251

def expand?
  !!@expand
end

#finalize!Object

Invoke window finalization routine by destroying it and all children


237
238
239
240
241
242
243
244
245
246
247
# File 'lib/reterm/window.rb', line 237

def finalize!
  erase
  @@registry.delete(self)

  children.each { |c|
    del_child c
  }

  cdk_scr.destroy if cdk?
  component.finalize! if component?
end

#first_child?Boolean

Return boolean indicating if this window is the first child of its parent

Returns:

  • (Boolean)

73
74
75
76
# File 'lib/reterm/window.rb', line 73

def first_child?
  return true unless parent?
  parent.children.index(self) == 0
end

#getchObject

Blocking call to capture next character from window


428
429
430
# File 'lib/reterm/window.rb', line 428

def getch
  @win.getch
end

#last_child?Boolean

Return boolean indicating if this window is the last child of its parent

Returns:

  • (Boolean)

80
81
82
83
# File 'lib/reterm/window.rb', line 80

def last_child?
  return true unless parent?
  parent.children.index(self) == (parent.children.size - 1)
end

#must_expand?Boolean

Returns:

  • (Boolean)

255
256
257
# File 'lib/reterm/window.rb', line 255

def must_expand?
  !!@must_expand
end

#mvaddstr(*a) ⇒ Object

Write string at specified loc


455
456
457
# File 'lib/reterm/window.rb', line 455

def mvaddstr(*a)
  @win.mvaddstr(*a)
end

#no_bold!Object

Disable bold formatting


469
470
471
# File 'lib/reterm/window.rb', line 469

def no_bold!
  @win.attroff(Ncurses::A_BOLD)
end

#no_border!Object

Remove Border around window


418
419
420
# File 'lib/reterm/window.rb', line 418

def no_border!
  @win.border(' '.ord, ' '.ord, ' '.ord, ' '.ord, ' '.ord, ' '.ord, ' '.ord, ' '.ord)
end

#no_reverse!Object

Disabled reverse formatting


483
484
485
# File 'lib/reterm/window.rb', line 483

def no_reverse!
  @win.attroff(Ncurses::A_REVERSE)
end

#noutrefreshObject


408
409
410
411
412
413
414
415
# File 'lib/reterm/window.rb', line 408

def noutrefresh
  @win.noutrefresh
  children.each { |c|
    c.noutrefresh
  }

  component.component.screen.noutrefresh if cdk?
end

#parent?Boolean

Return boolean if this window is a child of another

Returns:

  • (Boolean)

62
63
64
# File 'lib/reterm/window.rb', line 62

def parent?
  !!@parent
end

#refreshObject

Refresh / resynchronize window and all children


399
400
401
402
403
404
405
406
# File 'lib/reterm/window.rb', line 399

def refresh
  @win.refresh
  children.each { |c|
    c.refresh
  }

  component.component.screen.refresh if cdk?
end

#request_expansion(r, c) ⇒ Object


259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/reterm/window.rb', line 259

def request_expansion(r, c)
  h = {:rows => r,
       :cols => c,
       :x    => x,
       :y    => y}

  if parent?
    if parent.component.exceeds_bounds_with?(h)
      if parent.component.expandable?
        parent.component.expand(h)

      else
        raise ArgumentError, "cannot expand" if must_expand?
        return false
      end
    end

  else
    unless Terminal.contains?(h)
      raise ArgumentError, "terminal too small" if must_expand?
      return false
    end
  end

  resize(r, c)
  true
end

#resize(rows, cols) ⇒ Object

Raises:

  • (ArgumentError)

358
359
360
361
362
363
364
365
366
# File 'lib/reterm/window.rb', line 358

def resize(rows, cols)
  r = win.resize rows, cols
  raise ArgumentError, "could not resize window" if r == -1

  @rows = rows
  @cols = cols

  self
end

#reverse!Object

Enable reverse formatting


474
475
476
477
478
479
480
# File 'lib/reterm/window.rb', line 474

def reverse!
  @win.attron(Ncurses::A_REVERSE)
  return unless block_given?

  yield
  @win.attroff(Ncurses::A_REVERSE)
end

#rootObject

Return root window (recusively), self if parent is not set


86
87
88
# File 'lib/reterm/window.rb', line 86

def root
  parent? ? parent.root : self
end

#sync!Object

Dispatch to component synchronization


447
448
449
450
451
452
# File 'lib/reterm/window.rb', line 447

def sync!
  component.sync! if component?
  children.each { |c|
    c.sync!
  }
end

#sync_getchObject

Normal getch unless sync enabled in which case, timeout will be checked & components synchronized


434
435
436
437
438
439
440
441
442
443
444
# File 'lib/reterm/window.rb', line 434

def sync_getch
  return self.getch unless sync_enabled?

  c = -1
  while c == -1 && !shutdown?
    c = self.getch
    run_sync!
  end

  c
end

#total_xObject


90
91
92
# File 'lib/reterm/window.rb', line 90

def total_x
  @tx ||= parent? ? (parent.total_x + x) : x
end

#total_yObject


94
95
96
# File 'lib/reterm/window.rb', line 94

def total_y
  @ty ||= parent? ? (parent.total_y + y) : y
end