text-ui 0.1.0
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.
- checksums.yaml +7 -0
- data/README.md +33 -0
- data/lib/tui/assets.rb +13 -0
- data/lib/tui/block.rb +142 -0
- data/lib/tui/format.rb +150 -0
- data/lib/tui/layout.rb +21 -0
- data/lib/tui/settings.rb +13 -0
- data/lib/tui.rb +94 -0
- metadata +58 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 412150d9f77ef6b3363eaae39bcc64a4ddc73a5a9ed3fa76a54841eca070549a
|
|
4
|
+
data.tar.gz: 9ae1904192edd13412b87667a9b0bf62c1ee601facc498feba703f5e85750d9a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 05ded2430b82482f2e7d3d4074159a9bec9684304bc4db69913ff3f9d0562664641d31c593a22b661c5cb30c9b91111c53c41c9bd93578c921cb00c036a15b1d
|
|
7
|
+
data.tar.gz: d46560ec1bdd4cfaa563b6f9b978b3276565f721d451ae5fce5ac302805ac028708fb2f6486e4d551b52ca4ff9b4e7e241a9e635bdaf34f8069a42c3e76e2af6
|
data/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Description
|
|
2
|
+
|
|
3
|
+
A gem to build Text User Interface of nested boxes for console with custom dynamic content.
|
|
4
|
+
|
|
5
|
+
It allows to positions and align nested boxes through notions of rows and columns to structure data.
|
|
6
|
+
|
|
7
|
+
## Usage
|
|
8
|
+
|
|
9
|
+
The gem expects you to have the main application that does its job separately.
|
|
10
|
+
|
|
11
|
+
In a meanwhile, TUI's thread would re-draw layout with you custom content on console.
|
|
12
|
+
|
|
13
|
+
See [example](./bin/example.rb) for some usage.
|
|
14
|
+
|
|
15
|
+
## Development
|
|
16
|
+
|
|
17
|
+
After checking out the repo, run `bundle install` to install dependencies. Then, run `ruby -Ilib:test test/test_tui.rb` to run the tests.
|
|
18
|
+
|
|
19
|
+
## Contributing
|
|
20
|
+
|
|
21
|
+
Feel free to submit bug reports and merge requests.
|
|
22
|
+
|
|
23
|
+
# Docs
|
|
24
|
+
|
|
25
|
+
`yard doc` generates some documentation.
|
|
26
|
+
|
|
27
|
+
`yard server --reload` starts a server with documentation available at http://localhost:8808
|
|
28
|
+
|
|
29
|
+
# TODOs
|
|
30
|
+
|
|
31
|
+
- resurrect logging
|
|
32
|
+
- handle Ctrl+C
|
|
33
|
+
- provide interface to control data fetching: frequency, caching, make it parallel
|
data/lib/tui/assets.rb
ADDED
data/lib/tui/block.rb
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'format'
|
|
4
|
+
|
|
5
|
+
module Tui
|
|
6
|
+
# Basic TUI building block.
|
|
7
|
+
#
|
|
8
|
+
# In a nutshell, all blocks are columns ({Block#column}) with {::String}s in it printed vertically one by one (see {#to_s}).
|
|
9
|
+
# So a row is a way to compose multiple columns to a single "column" (see {Block#row}), which is an {::Array} of lines anyway.
|
|
10
|
+
class Block
|
|
11
|
+
|
|
12
|
+
attr_reader :width
|
|
13
|
+
attr_reader :array
|
|
14
|
+
|
|
15
|
+
# Mix-in formatting methods.
|
|
16
|
+
# The {Blocks} class has only methods to make nested blocks
|
|
17
|
+
include Format
|
|
18
|
+
|
|
19
|
+
# Make a column with several rows in it. Rows could be other {Block}s or just {::String}s.
|
|
20
|
+
#
|
|
21
|
+
# The method actually transform array or "rows" to a single column right away.
|
|
22
|
+
# @example
|
|
23
|
+
# Block.column "some", "other" # "some" and "other will be centered in the column by default
|
|
24
|
+
# Block.column "some", "other", align: :left # "some" and "other" will be aligned to the left
|
|
25
|
+
# Block.column
|
|
26
|
+
# Block.row("one", "two"),
|
|
27
|
+
# "three"
|
|
28
|
+
# ]} # "one" and "two" will be printed in the first row, "three" will be below them centered
|
|
29
|
+
# Block.column("some", "other") { |el| el.box! } # box both string then place boxes to a column
|
|
30
|
+
#
|
|
31
|
+
# @param rows array of rows ({Block}s / {::String}s)
|
|
32
|
+
# @param align how to align blocks between each other: :center (default), :right, :left
|
|
33
|
+
# @param block individual row processor, the block is supplied with {Block}s
|
|
34
|
+
def self.column *rows, align: :center, &block
|
|
35
|
+
# row could be a String, make an array of horizontal lines from it
|
|
36
|
+
rows.collect! { |col| col.is_a?(Block) ? col : Block.new(col) }
|
|
37
|
+
rows.collect!(&block) if block_given? # pre-process "rows"
|
|
38
|
+
max_row_width = rows.collect(&:width).max
|
|
39
|
+
Block.new rows.collect! { |blk|
|
|
40
|
+
extra_columns = max_row_width - blk.width
|
|
41
|
+
case align
|
|
42
|
+
when :left then blk.collect! { |line| line + ' ' * extra_columns }
|
|
43
|
+
when :right then blk.collect! { |line| ' ' * extra_columns + line }
|
|
44
|
+
else
|
|
45
|
+
blk.h_pad!(extra_columns / 2)
|
|
46
|
+
extra_columns.odd? ? blk.collect! { |line| line + ' ' } : blk
|
|
47
|
+
end
|
|
48
|
+
blk.array # get the array to join using builtin flatten
|
|
49
|
+
}.flatten!
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Compose a row of other {Block}s or {::String}s.
|
|
53
|
+
#
|
|
54
|
+
# The method actually squashes a row of columns (blocks) to a single block (column)
|
|
55
|
+
# @example
|
|
56
|
+
# Block.row "some", "other" # => "someother"
|
|
57
|
+
# Block.row "some", ["other", "foo"], aligh: bottom # "some" will be shifted down by 1 line
|
|
58
|
+
# Block.row("some", "other") { |blk| blk.box! } # both columns will be enclosed in a box
|
|
59
|
+
#
|
|
60
|
+
# @param cols array of columns ({Block}s / {::String}s) to squash
|
|
61
|
+
# @param align how to align blocks in the row: :center, :top, :bottom
|
|
62
|
+
# @param block individual column processor
|
|
63
|
+
def self.row *cols, align: :center, &block
|
|
64
|
+
cols.collect! { |col| col.is_a?(Block) ? col : Block.new(col) }
|
|
65
|
+
cols.collect!(&block) if block_given? # pre-process columns
|
|
66
|
+
max_col_height = cols.collect(&:height).max
|
|
67
|
+
Block.new cols.collect! { |col|
|
|
68
|
+
extra_lines = max_col_height - col.height
|
|
69
|
+
case align
|
|
70
|
+
when :top then col << Array.new(extra_lines, '')
|
|
71
|
+
when :bottom then col >> Array.new(extra_lines, '')
|
|
72
|
+
else
|
|
73
|
+
col.v_pad!(extra_lines / 2)
|
|
74
|
+
col << '' if extra_lines.odd?
|
|
75
|
+
end
|
|
76
|
+
col.v_align! # is needed due to transpose call below
|
|
77
|
+
col.array # get the array to process using builtin methods
|
|
78
|
+
}.transpose.collect(&:join)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# "Render" the Block to print to the console.
|
|
82
|
+
# As each block and operation just transforms a list of Strings,
|
|
83
|
+
# the whole "rendering" is as simple as ...
|
|
84
|
+
def to_s
|
|
85
|
+
@array.join "\n"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Add extra lines from the supplied array to the block;
|
|
89
|
+
# no auto-alignment is performed, see {#v_align!} to make width even
|
|
90
|
+
# @param other either {::Array} or {::String} to push back
|
|
91
|
+
def << other
|
|
92
|
+
other.is_a?(Array) ? @array += other : @array << other
|
|
93
|
+
@width = @array.collect(&:size).max
|
|
94
|
+
self
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Add extra lines to the "start" of the block
|
|
98
|
+
# @param other either {::Array} or {::String} to push_forward
|
|
99
|
+
# @example
|
|
100
|
+
# Block.column '1'
|
|
101
|
+
# block << %w[2 3] # now block has %w[2 3 1]
|
|
102
|
+
def >> other
|
|
103
|
+
case other
|
|
104
|
+
when Array
|
|
105
|
+
@width = [@width, other.collect(&:size).max].max
|
|
106
|
+
other.reverse_each { |i|
|
|
107
|
+
@array.unshift i
|
|
108
|
+
}
|
|
109
|
+
when String
|
|
110
|
+
@width = [@width, other.size].max
|
|
111
|
+
@array.unshift other
|
|
112
|
+
end
|
|
113
|
+
self
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Get {Block}'s height in symbols
|
|
117
|
+
# Block is a column => column's height is {Block}'s array size
|
|
118
|
+
def height
|
|
119
|
+
@array.size
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Modify each "row" of the {Block} inline
|
|
123
|
+
def collect! &block
|
|
124
|
+
@array.collect!(&block)
|
|
125
|
+
@width = @array.collect(&:size).max
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
# Constructor is private.
|
|
131
|
+
#
|
|
132
|
+
# {#column} and {#row} are to make blocks;
|
|
133
|
+
def initialize arg
|
|
134
|
+
@array = case arg
|
|
135
|
+
when Array then arg
|
|
136
|
+
when String then [arg]
|
|
137
|
+
else [arg.to_s]
|
|
138
|
+
end
|
|
139
|
+
@width = @array.collect(&:size).max
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
data/lib/tui/format.rb
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tui
|
|
4
|
+
# Collection of low-level {Block} formatting methods.
|
|
5
|
+
#
|
|
6
|
+
# Most methods
|
|
7
|
+
# - mutate the object they are called for => no need to re-assign
|
|
8
|
+
# - `return self` => are intended to be chained (`block.box!.v_pad!(2)`)
|
|
9
|
+
#
|
|
10
|
+
# The module is not to be used directly
|
|
11
|
+
# and exists just to de-couple formatting methods from the rest.
|
|
12
|
+
module Format
|
|
13
|
+
|
|
14
|
+
# Add horizontal (to the sides) padding to the block.
|
|
15
|
+
# @param size number of spaces to add
|
|
16
|
+
def h_pad! size = 1
|
|
17
|
+
collect! { |line|
|
|
18
|
+
"#{' ' * size}#{line}#{' ' * size}"
|
|
19
|
+
}
|
|
20
|
+
@width += size * 2
|
|
21
|
+
self
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Add vertical padding (before-after) to the block
|
|
25
|
+
# @param size number of spaces to add
|
|
26
|
+
def v_pad! size = 1
|
|
27
|
+
filler = ' ' * @width
|
|
28
|
+
size.times {
|
|
29
|
+
self >> filler
|
|
30
|
+
self << filler
|
|
31
|
+
}
|
|
32
|
+
self
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Adds spaces around the block
|
|
36
|
+
# @param size number of spaces to add
|
|
37
|
+
def pad! size = 1
|
|
38
|
+
# order matters!
|
|
39
|
+
v_pad! size
|
|
40
|
+
h_pad! size
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Aligns block elements vertically (by width) by adding spaces.
|
|
44
|
+
#
|
|
45
|
+
# Lines (rows) within the block can be of uneven width.
|
|
46
|
+
# This method changes all lines to have the same # of chars
|
|
47
|
+
# @param type {Symbol} :left, :center, :right
|
|
48
|
+
# @param width {Integer} target block width, is ignored if less than @width (look at {fit!})
|
|
49
|
+
def v_align! type = :left, width: @width
|
|
50
|
+
line_transformer = case type
|
|
51
|
+
when :center
|
|
52
|
+
->(line, num_spaces) { ' ' * (num_spaces / 2) + line.to_s + (' ' * (num_spaces / 2)) + (num_spaces.odd? ? ' ' : '') }
|
|
53
|
+
when :right
|
|
54
|
+
->(line, num_spaces) { (' ' * num_spaces) + line.to_s }
|
|
55
|
+
else # :left
|
|
56
|
+
->(line, num_spaces) { line.to_s + (' ' * num_spaces) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
return self if width.nil? || @width > width # == case makes all lines width even
|
|
60
|
+
|
|
61
|
+
@width = width
|
|
62
|
+
@array.collect! { |line| line_transformer.call line, @width - line.size }
|
|
63
|
+
self
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Aligns block elements horisontally (by height) by adding spaces.
|
|
67
|
+
#
|
|
68
|
+
# New lines get added to the block to have the specified # of lines in total.
|
|
69
|
+
# @param type {Symbol} :top, :center, :bottom
|
|
70
|
+
# @param height {Integer} target block height, is ignored if less than @width (look at {fit!})
|
|
71
|
+
def h_align! type = :top, height: @height
|
|
72
|
+
return self if height.nil? or @array.size > height
|
|
73
|
+
|
|
74
|
+
extra_lines_count = height - @array.size
|
|
75
|
+
case type
|
|
76
|
+
when :center
|
|
77
|
+
(extra_lines_count/2).times { @array.prepend ' ' * @width }
|
|
78
|
+
(extra_lines_count/2 + (extra_lines_count.odd? ? 1 : 0)).times { @array.append ' ' * @width }
|
|
79
|
+
when :top
|
|
80
|
+
(extra_lines_count).times { @array.append ' ' * @width }
|
|
81
|
+
else # :bottom
|
|
82
|
+
(extra_lines_count).times { @array.prepend ' ' * @width }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
self
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Align content to the center of the specified width and height.
|
|
89
|
+
#
|
|
90
|
+
# @param height {Number} target height
|
|
91
|
+
# @param width {Number} target width
|
|
92
|
+
def align! height: nil, width: nil
|
|
93
|
+
v_align! :center, width: width
|
|
94
|
+
h_align! :center, height: height
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Add a square box around the block.
|
|
98
|
+
#
|
|
99
|
+
# It auto-aligns the block, so use {v_align!} beforehand!
|
|
100
|
+
# if you want custom alignment for the block
|
|
101
|
+
def box! corners: :round
|
|
102
|
+
corners = Assets::CORNERS[corners]
|
|
103
|
+
lines = Assets::LINES[:single]
|
|
104
|
+
v_align!
|
|
105
|
+
@array.collect! { |line| "#{lines[0]}#{line}#{lines[0]}" }
|
|
106
|
+
@array.unshift "#{corners[0]}#{lines[1] * width}#{corners[1]}"
|
|
107
|
+
@array << "#{corners[2]}#{lines[1] * width}#{corners[3]}"
|
|
108
|
+
@width += 2
|
|
109
|
+
self
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Fit the current block to a rectangle by
|
|
113
|
+
# cropping the block and adding a special markers to its content.
|
|
114
|
+
#
|
|
115
|
+
# Actual content width and height will be 1 char less to store cropping symbols too.
|
|
116
|
+
#
|
|
117
|
+
# Filling does not align content, {v_align!} does.
|
|
118
|
+
#
|
|
119
|
+
# @param width width to fit, nil => don't touch width
|
|
120
|
+
# @param height height to fit, nil => don't touch height
|
|
121
|
+
# @param fill whether to fill {Block} to be of the size of the box
|
|
122
|
+
def fit! width: nil, height: nil, fill: false
|
|
123
|
+
# pre-calc width to use below
|
|
124
|
+
@width = width unless width.nil? || (@width < width && !fill)
|
|
125
|
+
|
|
126
|
+
unless height.nil?
|
|
127
|
+
if @array.size > height
|
|
128
|
+
@array.slice!((height - 1)..)
|
|
129
|
+
@array << ('░' * @width)
|
|
130
|
+
elsif fill && @array.size < height
|
|
131
|
+
@array += Array.new(height - @array.size, ' ' * @width)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
unless width.nil?
|
|
135
|
+
collect! { |line|
|
|
136
|
+
if line.size > width
|
|
137
|
+
"#{line[...(width - 1)]}░"
|
|
138
|
+
elsif fill && line.size < width
|
|
139
|
+
extra = (width - line.size)
|
|
140
|
+
"#{' ' * (extra/2)}#{line}#{' ' * (extra/2 + (extra.odd? ? 1 : 0))}"
|
|
141
|
+
else
|
|
142
|
+
line
|
|
143
|
+
end
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
self
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
end
|
|
150
|
+
end
|
data/lib/tui/layout.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative 'block'
|
|
2
|
+
|
|
3
|
+
# Main window layout.
|
|
4
|
+
#
|
|
5
|
+
# It represents the main window of the application's TUI
|
|
6
|
+
class Tui::Layout
|
|
7
|
+
# Internal content is expected to be rendered each time => should be callable
|
|
8
|
+
# @example
|
|
9
|
+
# Tui::Layout.new { Block.column "foo", "bar" }
|
|
10
|
+
def initialize &block
|
|
11
|
+
@root = block || -> { Tui::Block::column "Hello TUI!" }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Render content to produce a frame, which could be print -ed.
|
|
15
|
+
def render
|
|
16
|
+
box = @root.call
|
|
17
|
+
raise "main window is not a block but #{box.class}" unless box.is_a? Tui::Block
|
|
18
|
+
|
|
19
|
+
box
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/tui/settings.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Settings for the TUI.
|
|
2
|
+
#
|
|
3
|
+
# - target_fps - how frequently you want the content to refresh. It's useful to control CPU, console I/O and data fetching usage
|
|
4
|
+
# - window_size - size of the main window, e.g. `{ height: 70, width: 200 }`
|
|
5
|
+
# - draw_main_window - whether to fit content in a box and center it by default within window
|
|
6
|
+
Tui::Settings = Data.define(:target_fps, :window_size, :draw_main_window) do
|
|
7
|
+
def initialize(
|
|
8
|
+
target_fps: 30,
|
|
9
|
+
window_size: { height: 70, width: 200 }.freeze,
|
|
10
|
+
draw_main_window: true)
|
|
11
|
+
super(target_fps:, window_size:, draw_main_window:)
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/tui.rb
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Tui; end
|
|
4
|
+
|
|
5
|
+
require 'date'
|
|
6
|
+
require 'io/console'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
require_relative 'tui/assets'
|
|
10
|
+
require_relative 'tui/block'
|
|
11
|
+
require_relative 'tui/format'
|
|
12
|
+
require_relative 'tui/layout'
|
|
13
|
+
require_relative 'tui/settings'
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# Root module / singleton. See {Tui#run} - the main entrypoint.
|
|
17
|
+
module Tui
|
|
18
|
+
# Delay correction step to maintain FPS
|
|
19
|
+
DELAY_STEP_MS = 100
|
|
20
|
+
|
|
21
|
+
# Frames counter to calculate average FPS
|
|
22
|
+
@frames_count = 0
|
|
23
|
+
|
|
24
|
+
class << self
|
|
25
|
+
|
|
26
|
+
# Current delay between redraws
|
|
27
|
+
attr_reader :delay_ms
|
|
28
|
+
# Average redraws count / sec
|
|
29
|
+
attr_reader :current_avg_fps
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# The main entrypoint of the TUI
|
|
33
|
+
# @param layout {Tui::Layout} - UI's layout to render and draw
|
|
34
|
+
# @param settings {Tui::Settings} - UI's settings that affects UI's behavior
|
|
35
|
+
# @return {Thread}
|
|
36
|
+
def run layout, settings = Settings::new
|
|
37
|
+
raise "expected Layout, found #{layout.class}" unless layout.is_a? Layout
|
|
38
|
+
|
|
39
|
+
@settings = settings
|
|
40
|
+
@started_at = DateTime.now.strftime('%Q').to_i
|
|
41
|
+
@delay_ms = 1000 / @settings.target_fps
|
|
42
|
+
|
|
43
|
+
@thread = Thread.new {
|
|
44
|
+
loop {
|
|
45
|
+
clear
|
|
46
|
+
refresh
|
|
47
|
+
print @settings.draw_main_window ?
|
|
48
|
+
layout.render
|
|
49
|
+
.align!(**effective_window_size)
|
|
50
|
+
.fit!(**effective_window_size, fill: true)
|
|
51
|
+
.box!
|
|
52
|
+
: layout.render
|
|
53
|
+
|
|
54
|
+
sleep @delay_ms / 1000.0
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@thread
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def clear
|
|
64
|
+
print "\e[2J\e[f"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Refresh terminal size and adjust delay
|
|
68
|
+
def refresh
|
|
69
|
+
ms_spent = 1 + DateTime.now.strftime('%Q').to_i - @started_at
|
|
70
|
+
|
|
71
|
+
@current_avg_fps = @frames_count * 1000.0 / ms_spent
|
|
72
|
+
|
|
73
|
+
# TODO: use progression
|
|
74
|
+
if @settings.target_fps > @current_avg_fps && @delay_ms > DELAY_STEP_MS
|
|
75
|
+
@delay_ms -= DELAY_STEP_MS
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
if @settings.target_fps < @current_avg_fps && @delay_ms < 1000
|
|
79
|
+
@delay_ms += DELAY_STEP_MS
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
@frames_count += 1;
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Calculate actual content size from settings and available terminal's shape
|
|
86
|
+
def effective_window_size
|
|
87
|
+
{
|
|
88
|
+
height: (IO.console&.winsize&.first.nil? || @settings.window_size[:height] < IO.console.winsize.first ? @settings.window_size[:height] : IO.console.winsize.first) - 4,
|
|
89
|
+
width: (IO.console&.winsize&.last.nil? || @settings.window_size[:width] < IO.console.winsize.last ? @settings.window_size[:width] : IO.console.winsize.last) - 2
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: text-ui
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Skorobogaty Dmitry
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-12-31 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: |2
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
A gem to build Text User Interface of nested boxes for console with custom dynamic content.
|
|
17
|
+
|
|
18
|
+
It allows to positions and align nested boxes through notions of rows and columns to structure data.
|
|
19
|
+
email:
|
|
20
|
+
- skorobogaty.dmitry@gmail.com
|
|
21
|
+
executables: []
|
|
22
|
+
extensions: []
|
|
23
|
+
extra_rdoc_files: []
|
|
24
|
+
files:
|
|
25
|
+
- README.md
|
|
26
|
+
- lib/tui.rb
|
|
27
|
+
- lib/tui/assets.rb
|
|
28
|
+
- lib/tui/block.rb
|
|
29
|
+
- lib/tui/format.rb
|
|
30
|
+
- lib/tui/layout.rb
|
|
31
|
+
- lib/tui/settings.rb
|
|
32
|
+
homepage: https://github.com/skorobogatydmitry/tui
|
|
33
|
+
licenses:
|
|
34
|
+
- LGPL-3.0-only
|
|
35
|
+
metadata:
|
|
36
|
+
homepage_uri: https://github.com/skorobogatydmitry/tui
|
|
37
|
+
source_code_uri: https://github.com/skorobogatydmitry/tui
|
|
38
|
+
allowed_push_host: https://rubygems.org/
|
|
39
|
+
post_install_message:
|
|
40
|
+
rdoc_options: []
|
|
41
|
+
require_paths:
|
|
42
|
+
- lib
|
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.3'
|
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
|
+
requirements:
|
|
50
|
+
- - ">="
|
|
51
|
+
- !ruby/object:Gem::Version
|
|
52
|
+
version: '0'
|
|
53
|
+
requirements: []
|
|
54
|
+
rubygems_version: 3.5.11
|
|
55
|
+
signing_key:
|
|
56
|
+
specification_version: 4
|
|
57
|
+
summary: Draw nested boxes in console.
|
|
58
|
+
test_files: []
|