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)


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

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?



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

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



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/reterm/window.rb', line 198

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



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

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

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

Adjusts rows / cols so as to fill parent



215
216
217
218
219
220
221
222
223
224
225
# File 'lib/reterm/window.rb', line 215

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



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

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

Instance Method Details

#activate!(*input) ⇒ Object

Activate window component



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

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

#actual_colsObject

Return window cols



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

def actual_cols
  dimensions[1]
end

#actual_rowsObject

Return window rows



509
510
511
# File 'lib/reterm/window.rb', line 509

def actual_rows
  dimensions[0]
end

#bold!Object

Enable bold formatting



454
455
456
457
458
459
460
# File 'lib/reterm/window.rb', line 454

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

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

#border!Object

Draw Border around window



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

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

#cdk?Boolean

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

Returns:

  • (Boolean)


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

def cdk?
  !!@cdk_scr
end

#cdk_scrObject

Return cdk screen (only used by CDK components)



284
285
286
287
# File 'lib/reterm/window.rb', line 284

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



330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/reterm/window.rb', line 330

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



365
366
367
368
369
370
371
372
# File 'lib/reterm/window.rb', line 365

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

  @children = []
  erase
end

#colored?Boolean

Return bool indiciating if colors are set

Returns:

  • (Boolean)


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

def colored?
  !!@colors
end

#colors=(c) ⇒ Object

Set window color



487
488
489
490
491
492
493
494
495
496
# File 'lib/reterm/window.rb', line 487

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.



313
314
315
316
317
318
# File 'lib/reterm/window.rb', line 313

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



322
323
324
325
326
327
# File 'lib/reterm/window.rb', line 322

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



499
500
501
502
503
504
505
506
# File 'lib/reterm/window.rb', line 499

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

#draw!Object

Draw component in window



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

def draw!
  component.draw! if component?
end

#eraseObject

Erase window drawing area



375
376
377
378
379
380
381
382
383
# File 'lib/reterm/window.rb', line 375

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



386
387
388
389
390
# File 'lib/reterm/window.rb', line 386

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

#expand?Boolean

Returns:

  • (Boolean)


245
246
247
# File 'lib/reterm/window.rb', line 245

def expand?
  !!@expand
end

#finalize!Object

Invoke window finalization routine by destroying it and all children



231
232
233
234
235
236
237
238
239
240
241
# File 'lib/reterm/window.rb', line 231

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



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

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)


249
250
251
# File 'lib/reterm/window.rb', line 249

def must_expand?
  !!@must_expand
end

#mvaddstr(*a) ⇒ Object

Write string at specified loc



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

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

#no_bold!Object

Disable bold formatting



463
464
465
# File 'lib/reterm/window.rb', line 463

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

#no_border!Object

Remove Border around window



412
413
414
# File 'lib/reterm/window.rb', line 412

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

#no_reverse!Object

Disabled reverse formatting



477
478
479
# File 'lib/reterm/window.rb', line 477

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

#noutrefreshObject



402
403
404
405
406
407
408
409
# File 'lib/reterm/window.rb', line 402

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



393
394
395
396
397
398
399
400
# File 'lib/reterm/window.rb', line 393

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

  component.component.screen.refresh if cdk?
end

#request_expansion(r, c) ⇒ Object



253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/reterm/window.rb', line 253

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)


352
353
354
355
356
357
358
359
360
# File 'lib/reterm/window.rb', line 352

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



468
469
470
471
472
473
474
# File 'lib/reterm/window.rb', line 468

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



441
442
443
444
445
446
# File 'lib/reterm/window.rb', line 441

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



428
429
430
431
432
433
434
435
436
437
438
# File 'lib/reterm/window.rb', line 428

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