termgui 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +14 -0
- data/LICENSE +19 -0
- data/README.md +321 -0
- data/TODO.md +259 -0
- data/src/action.rb +58 -0
- data/src/box.rb +90 -0
- data/src/color.rb +174 -0
- data/src/cursor.rb +69 -0
- data/src/editor/editor_base.rb +152 -0
- data/src/editor/editor_base_handlers.rb +116 -0
- data/src/element.rb +61 -0
- data/src/element_bounds.rb +111 -0
- data/src/element_box.rb +64 -0
- data/src/element_render.rb +102 -0
- data/src/element_style.rb +51 -0
- data/src/emitter.rb +102 -0
- data/src/emitter_state.rb +19 -0
- data/src/enterable.rb +93 -0
- data/src/event.rb +92 -0
- data/src/focus.rb +102 -0
- data/src/geometry.rb +53 -0
- data/src/image.rb +60 -0
- data/src/input.rb +85 -0
- data/src/input_grab.rb +17 -0
- data/src/input_time.rb +97 -0
- data/src/key.rb +114 -0
- data/src/log.rb +24 -0
- data/src/node.rb +117 -0
- data/src/node_attributes.rb +27 -0
- data/src/node_visit.rb +52 -0
- data/src/renderer.rb +119 -0
- data/src/renderer_cursor.rb +18 -0
- data/src/renderer_draw.rb +28 -0
- data/src/renderer_image.rb +31 -0
- data/src/renderer_print.rb +40 -0
- data/src/screen.rb +96 -0
- data/src/screen_element.rb +59 -0
- data/src/screen_input.rb +43 -0
- data/src/screen_renderer.rb +53 -0
- data/src/style.rb +149 -0
- data/src/tco/colouring.rb +248 -0
- data/src/tco/config.rb +57 -0
- data/src/tco/palette.rb +603 -0
- data/src/tco/tco_termgui.rb +30 -0
- data/src/termgui.rb +29 -0
- data/src/util.rb +110 -0
- data/src/util/css.rb +98 -0
- data/src/util/css_query.rb +23 -0
- data/src/util/easing.rb +364 -0
- data/src/util/hash_object.rb +131 -0
- data/src/util/imagemagick.rb +27 -0
- data/src/util/justify.rb +20 -0
- data/src/util/unicode-categories.rb +572 -0
- data/src/util/wrap.rb +102 -0
- data/src/widget/button.rb +33 -0
- data/src/widget/checkbox.rb +47 -0
- data/src/widget/col.rb +30 -0
- data/src/widget/image.rb +106 -0
- data/src/widget/inline.rb +40 -0
- data/src/widget/input_number.rb +73 -0
- data/src/widget/inputbox.rb +85 -0
- data/src/widget/label.rb +33 -0
- data/src/widget/modal.rb +69 -0
- data/src/widget/row.rb +26 -0
- data/src/widget/selectbox.rb +100 -0
- data/src/widget/textarea.rb +54 -0
- data/src/xml/xml.rb +80 -0
- data/test/action_test.rb +34 -0
- data/test/box_test.rb +15 -0
- data/test/css_test.rb +39 -0
- data/test/editor/editor_base_test.rb +201 -0
- data/test/element_bounds_test.rb +77 -0
- data/test/element_box_test.rb +8 -0
- data/test/element_render_test.rb +124 -0
- data/test/element_style_test.rb +85 -0
- data/test/element_test.rb +10 -0
- data/test/emitter_test.rb +108 -0
- data/test/event_test.rb +19 -0
- data/test/focus_test.rb +37 -0
- data/test/geometry_test.rb +12 -0
- data/test/input_test.rb +47 -0
- data/test/key_test.rb +14 -0
- data/test/log_test.rb +21 -0
- data/test/node_test.rb +105 -0
- data/test/performance/performance1.rb +48 -0
- data/test/renderer_test.rb +74 -0
- data/test/renderer_test_rect.rb +4 -0
- data/test/screen_test.rb +58 -0
- data/test/style_test.rb +18 -0
- data/test/termgui_test.rb +10 -0
- data/test/test_all.rb +30 -0
- data/test/util_hash_object_test.rb +93 -0
- data/test/util_test.rb +26 -0
- data/test/widget/checkbox_test.rb +99 -0
- data/test/widget/col_test.rb +87 -0
- data/test/widget/inline_test.rb +40 -0
- data/test/widget/label_test.rb +94 -0
- data/test/widget/row_test.rb +40 -0
- data/test/wrap_test.rb +11 -0
- data/test/xml_test.rb +77 -0
- metadata +101 -1
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative 'geometry'
|
2
|
+
|
3
|
+
# so screen emulates an Element. TODO: make Screen < Element and get rid of width and height and all of this...
|
4
|
+
module ScreenElement
|
5
|
+
# complies with Element#render and also is capable of rendering given elements
|
6
|
+
def render(element = nil)
|
7
|
+
if element == self || element.nil?
|
8
|
+
children.each { |child| child.render self }
|
9
|
+
elsif !element.nil?
|
10
|
+
element.render self
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def abs_x
|
15
|
+
0
|
16
|
+
end
|
17
|
+
|
18
|
+
def abs_y
|
19
|
+
0
|
20
|
+
end
|
21
|
+
|
22
|
+
def abs_width
|
23
|
+
@width
|
24
|
+
end
|
25
|
+
|
26
|
+
def style
|
27
|
+
@style ||= Style.new
|
28
|
+
@style
|
29
|
+
end
|
30
|
+
|
31
|
+
def style=(s)
|
32
|
+
@style = s
|
33
|
+
end
|
34
|
+
|
35
|
+
def final_style
|
36
|
+
style
|
37
|
+
end
|
38
|
+
|
39
|
+
def offset
|
40
|
+
v = get_attribute('offset')
|
41
|
+
unless v
|
42
|
+
v = Offset.new
|
43
|
+
set_attribute('offset', v)
|
44
|
+
end
|
45
|
+
v
|
46
|
+
end
|
47
|
+
|
48
|
+
def offset=(value)
|
49
|
+
set_attribute('offset', value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def abs_height
|
53
|
+
@height
|
54
|
+
end
|
55
|
+
|
56
|
+
def root_screen
|
57
|
+
self
|
58
|
+
end
|
59
|
+
end
|
data/src/screen_input.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# adds Input related methods to screen
|
2
|
+
module ScreenInput
|
3
|
+
attr_accessor :exit_keys
|
4
|
+
|
5
|
+
# Analog to HTML DOM / Node.js setTimeout() using input event loop
|
6
|
+
# @param {Number} seconds
|
7
|
+
def set_timeout(seconds = @input.interval, listener = nil, &block)
|
8
|
+
the_listener = listener == nil ? block : listener
|
9
|
+
throw 'No listener provided' if the_listener == nil
|
10
|
+
@input.set_timeout(seconds, the_listener)
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear_timeout(listener)
|
14
|
+
@input.clear_timeout(listener)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Analog to HTML DOM / Node.js setInterval() using input event loop
|
18
|
+
# @param {Number} seconds
|
19
|
+
def set_interval(seconds = @input.interval, listener = nil, &block)
|
20
|
+
the_listener = listener == nil ? block : listener
|
21
|
+
throw 'No listener provided' if the_listener == nil
|
22
|
+
@input.set_interval(seconds, the_listener)
|
23
|
+
end
|
24
|
+
|
25
|
+
def clear_interval(listener)
|
26
|
+
@input.clear_interval(listener)
|
27
|
+
end
|
28
|
+
|
29
|
+
def install_exit_keys
|
30
|
+
return if @exit_keys_listener
|
31
|
+
|
32
|
+
@exit_keys_listener = @input.subscribe('key') do |e|
|
33
|
+
destroy if @exit_keys.include?(e.key)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def uninstall_exit_keys
|
38
|
+
return unless @exit_keys_listener
|
39
|
+
|
40
|
+
@input.off('key', @exit_keys_listener)
|
41
|
+
@exit_keys_listener = nil
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# adds rendering related methods to screen
|
2
|
+
module ScreenRenderer
|
3
|
+
# renders given text at given position
|
4
|
+
def text(x: 0, y: 0, text: ' ', style: nil)
|
5
|
+
write @renderer.text(x: x, y: y, text: text, style: style)
|
6
|
+
end
|
7
|
+
|
8
|
+
def rect(x: 0, y: 0, width: self.width, height: self.height, ch: Pixel.EMPTY_CH, style: nil)
|
9
|
+
write @renderer.rect(x: x, y: y, width: width, height: height, ch: ch, style: style)
|
10
|
+
end
|
11
|
+
|
12
|
+
def image(x: 0, y: 0, image: nil, ch: Pixel.EMPTY_CH, style: Style.new, fg: false, bg: true, transparent_color: nil, h: height - y, w: width - x)
|
13
|
+
write @renderer.image(x: x, y: y, image: image, ch: ch || Pixel.EMPTY_CH, style: style, fg: fg, bg: bg, transparent_color: transparent_color, h: h, w: w)
|
14
|
+
end
|
15
|
+
|
16
|
+
def circle(x: nil, y: nil, radius: nil, stroke_ch: ' ', stroke: nil, fill: nil, fill_ch: stroke_ch)
|
17
|
+
write @renderer.circle(x: x, y: y, radius: radius, stroke_ch: stroke_ch, stroke: stroke, fill: fill, fill_ch: fill_ch)
|
18
|
+
end
|
19
|
+
|
20
|
+
def clear
|
21
|
+
@renderer.style = Style.new
|
22
|
+
write "#{@renderer.clear}#{@renderer.style.print}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def style=(style)
|
26
|
+
@renderer.style = style
|
27
|
+
write @renderer.style.print
|
28
|
+
end
|
29
|
+
|
30
|
+
def box(x, y, width, height, border_style = :classic, style = nil, content = ' ')
|
31
|
+
self.style = style if style
|
32
|
+
box = draw_box(width: width, height: height, style: border_style, content: content)
|
33
|
+
(box.map.with_index do |line, index|
|
34
|
+
text(x: x, y: y + index, text: line, style: style)
|
35
|
+
end).join('')
|
36
|
+
end
|
37
|
+
|
38
|
+
def print
|
39
|
+
@renderer.print
|
40
|
+
end
|
41
|
+
|
42
|
+
def cursor_move(x, y)
|
43
|
+
write @renderer.move(x, y)
|
44
|
+
end
|
45
|
+
|
46
|
+
def cursor_show
|
47
|
+
write @renderer.cursor_show
|
48
|
+
end
|
49
|
+
|
50
|
+
def cursor_hide
|
51
|
+
write @renderer.cursor_hide
|
52
|
+
end
|
53
|
+
end
|
data/src/style.rb
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
require_relative 'util'
|
2
|
+
require_relative 'util/hash_object'
|
3
|
+
require_relative 'tco/tco_termgui'
|
4
|
+
|
5
|
+
module TermGui
|
6
|
+
# refers to properties directly implemented using ansi escape codes
|
7
|
+
# responsible of printing escape ansi codes for style
|
8
|
+
# Styles are data objects, supporting hash to instantiate, assign, equals, print
|
9
|
+
class BaseStyle
|
10
|
+
include HashObject
|
11
|
+
|
12
|
+
attr_accessor :fg, :bg, :underline, :bold, :blink, :inverse, :fraktur, :framed
|
13
|
+
|
14
|
+
def initialize(fg: nil, bg: nil, bold: nil, blink: nil, inverse: nil, underline: nil, framed: nil, fraktur: nil, bright: nil, wrap: nil, border: nil, padding: nil, style: nil)
|
15
|
+
# TODO: for some reason **args is not working here that's why we have all subclasses props
|
16
|
+
@fg = fg
|
17
|
+
@bg = bg
|
18
|
+
@bold = bold || bright
|
19
|
+
@blink = blink
|
20
|
+
@inverse = inverse
|
21
|
+
@underline = underline
|
22
|
+
@fraktur = fraktur
|
23
|
+
@framed = framed
|
24
|
+
end
|
25
|
+
|
26
|
+
# Prints the style as escape sequences.
|
27
|
+
# This method shouln't be overriden by subclasses since it only makes sense for basic properties defined here.
|
28
|
+
def print(s = nil)
|
29
|
+
if s == nil
|
30
|
+
TermGui.open_style(self)
|
31
|
+
else
|
32
|
+
TermGui.print(s, self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.fast_colouring(value)
|
37
|
+
TermGui.fast_colouring(value)
|
38
|
+
end
|
39
|
+
|
40
|
+
def reset
|
41
|
+
@bg = @fg = @wrap = @border = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# returns true if self has the same properties of given hash or Style and each property value is equals (comparission using ==)
|
45
|
+
def equals(style)
|
46
|
+
object_equal(self, BaseStyle.from_hash(style))
|
47
|
+
end
|
48
|
+
|
49
|
+
# if a hash is given returns a new Style instance with given properties. If an Style instance if given, returns it.
|
50
|
+
# It also ensures focus, action and enter properties are defined cloning self if not
|
51
|
+
def self.from_hash(obj)
|
52
|
+
if obj == nil
|
53
|
+
return nil
|
54
|
+
elsif obj.instance_of?(Hash)
|
55
|
+
r = merge_hash_into_object obj, new
|
56
|
+
else
|
57
|
+
r = obj
|
58
|
+
end
|
59
|
+
|
60
|
+
if r.is_a? Style
|
61
|
+
r.focus = r.focus || r.clone
|
62
|
+
r.border = Border.from_hash(r.border) if r.border
|
63
|
+
r.action = r.action || r.clone
|
64
|
+
r.enter = r.enter || r.clone
|
65
|
+
end
|
66
|
+
r
|
67
|
+
end
|
68
|
+
|
69
|
+
def pretty_print(delete_nil = true, delete_empty = true)
|
70
|
+
h = to_hash
|
71
|
+
h.keys.each do |k|
|
72
|
+
h.delete k if delete_nil && h[k] == nil
|
73
|
+
if delete_empty && (h[k].respond_to?(:to_hash) || h[k].is_a?(Hash)) && object_variables_to_hash(h[k]).keys.reject { |k| h[k] == nil }.empty?
|
74
|
+
h.delete k
|
75
|
+
end
|
76
|
+
end
|
77
|
+
"{#{h.keys.map { |k| "#{k}: #{pretty_print_value(h[k])}" }.join(', ')}}" .split(/, [^\s]+: \{\}/).join('')
|
78
|
+
end
|
79
|
+
|
80
|
+
def pretty_print_value(v)
|
81
|
+
v.is_a?(String) ? v : v.respond_to?(:pretty_print) ? v.pretty_print : v.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.from_json(s)
|
85
|
+
r = from_hash(json_parse(s))
|
86
|
+
if r.is_a? Style
|
87
|
+
r.border = from_hash(r.border || new)
|
88
|
+
r.focus = from_hash(r.focus || new)
|
89
|
+
r.enter = from_hash(r.enter || new)
|
90
|
+
r.action = from_hash(r.action || new)
|
91
|
+
end
|
92
|
+
r
|
93
|
+
end
|
94
|
+
|
95
|
+
def bright
|
96
|
+
@bold
|
97
|
+
end
|
98
|
+
|
99
|
+
def bright=(value)
|
100
|
+
@bold = value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Element style. This is the class of `element.style` - `get_attribute('style')``
|
105
|
+
class Style < BaseStyle
|
106
|
+
attr_accessor :border, :wrap, :padding, :focus, :enter, :action
|
107
|
+
|
108
|
+
def initialize(**args)
|
109
|
+
super
|
110
|
+
@wrap = args[:wrap]
|
111
|
+
# TODO: move this border checking & init to hash_object
|
112
|
+
if args[:border].nil?
|
113
|
+
@border = nil
|
114
|
+
elsif args[:border].instance_of? Border
|
115
|
+
@border = args[:border]
|
116
|
+
elsif args[:border].instance_of? Hash
|
117
|
+
@border = Border.new
|
118
|
+
@border.assign(args[:border])
|
119
|
+
end
|
120
|
+
padding = args[:padding]
|
121
|
+
focus = args[:focus] || clone
|
122
|
+
enter = args[:enter] || clone
|
123
|
+
action = args[:action] || clone
|
124
|
+
|
125
|
+
@padding = padding
|
126
|
+
@focus = focus
|
127
|
+
@enter = enter
|
128
|
+
@action = action
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# style for the border
|
133
|
+
class Border < BaseStyle
|
134
|
+
attr_reader :style
|
135
|
+
|
136
|
+
def initialize(**args)
|
137
|
+
super
|
138
|
+
@style = args[:style]&.to_s || 'single'
|
139
|
+
end
|
140
|
+
|
141
|
+
def style=(style)
|
142
|
+
@style = style&.to_s
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
BaseStyle = TermGui::BaseStyle
|
148
|
+
Border = TermGui::Border
|
149
|
+
Style = TermGui::Style
|
@@ -0,0 +1,248 @@
|
|
1
|
+
# rubocop:disable Layout/EndAlignment
|
2
|
+
|
3
|
+
# adapted from tco
|
4
|
+
# tco - terminal colouring application and library
|
5
|
+
# Copyright (c) 2013, 2014 Radek Pazdera
|
6
|
+
|
7
|
+
require_relative 'palette'
|
8
|
+
|
9
|
+
module Tco
|
10
|
+
class Colouring
|
11
|
+
ANSI_FG_BASE = 30
|
12
|
+
ANSI_BG_BASE = 40
|
13
|
+
|
14
|
+
attr_reader :palette
|
15
|
+
|
16
|
+
def initialize(configuration)
|
17
|
+
@palette = Palette.new configuration.options['palette']
|
18
|
+
@output_type = configuration.options['output']
|
19
|
+
@disabled = configuration.options['disabled']
|
20
|
+
|
21
|
+
configuration.colour_values.each do |id, value|
|
22
|
+
@palette.set_colour_value(parse_colour_id(id), parse_rgb_value(value))
|
23
|
+
end
|
24
|
+
|
25
|
+
@names = {}
|
26
|
+
configuration.names.each do |name, colour_def|
|
27
|
+
@names[name] = resolve_colour_def colour_def
|
28
|
+
end
|
29
|
+
|
30
|
+
@styles = {}
|
31
|
+
configuration.styles.each do |name, style|
|
32
|
+
@styles[name] = Style.new(resolve_colour_def(style[:fg]),
|
33
|
+
resolve_colour_def(style[:bg]),
|
34
|
+
style[:bright], style[:underline])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Decorate a string according to the style passed. The input string
|
39
|
+
# is processed line-by-line (the escape sequences are added to each
|
40
|
+
# line). This is due to some problems I've been having with some
|
41
|
+
# terminal emulators not handling multi-line coloured sequences well.
|
42
|
+
def decorate(string, style)
|
43
|
+
# (fg, bg, bright, underline)
|
44
|
+
# fg = style.fg
|
45
|
+
# bg = style.bg
|
46
|
+
# bright = style.bright
|
47
|
+
# underline = style.underline
|
48
|
+
return string if !STDOUT.isatty || @output_type == :raw || @disabled
|
49
|
+
|
50
|
+
fg = get_colour_instance style.fg
|
51
|
+
bg = get_colour_instance style.bg
|
52
|
+
|
53
|
+
output = []
|
54
|
+
lines = string.lines.map(&:chomp)
|
55
|
+
lines = [''] if lines.length.zero?
|
56
|
+
lines.each do |line|
|
57
|
+
unless line.length < 0
|
58
|
+
line = case @palette.type
|
59
|
+
when 'ansi' then colour_ansi line, fg, bg
|
60
|
+
when 'extended' then colour_extended line, fg, bg
|
61
|
+
else raise "Unknown palette '#{@palette.type}'."
|
62
|
+
end
|
63
|
+
|
64
|
+
line = e(1) + line if style.bright
|
65
|
+
line = e(4) + line if style.underline
|
66
|
+
line = e(5) + line if style.blink
|
67
|
+
line = e(7) + line if style.inverse
|
68
|
+
line = e(20) + line if style.fraktur
|
69
|
+
line = e(51) + line if style.framed
|
70
|
+
|
71
|
+
if (style.bright || style.underline || style.blink || style.inverse || style.fraktur || style.framed) && (fg == nil) && (bg == nil)
|
72
|
+
line << e(0)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
output.push line
|
77
|
+
end
|
78
|
+
|
79
|
+
output << '' if string =~ /\n$/
|
80
|
+
output.join "\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_style(name)
|
84
|
+
raise "Style '#{name}' not found." unless @styles.key? name
|
85
|
+
|
86
|
+
@styles[name]
|
87
|
+
end
|
88
|
+
|
89
|
+
def set_output(output_type)
|
90
|
+
raise "Output '#{output_type}' not supported." unless %i[term raw].include? output_type
|
91
|
+
|
92
|
+
@output_type = output_type
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_best_font_colour(background)
|
96
|
+
black = Tco::Colour.new([0, 0, 0])
|
97
|
+
white = Tco::Colour.new([255, 255, 255])
|
98
|
+
|
99
|
+
if background.is_a?(Colour) &&
|
100
|
+
(background - black).abs < (background - white).abs
|
101
|
+
return white
|
102
|
+
end
|
103
|
+
|
104
|
+
black
|
105
|
+
end
|
106
|
+
|
107
|
+
def get_colour_instance(value)
|
108
|
+
if value.is_a?(String)
|
109
|
+
resolve_colour_def value
|
110
|
+
elsif value.is_a?(Symbol)
|
111
|
+
resolve_colour_def value.to_s
|
112
|
+
elsif value.is_a?(Array)
|
113
|
+
Colour.new value
|
114
|
+
elsif value.is_a?(Colour) || value.is_a?(Unknown)
|
115
|
+
value
|
116
|
+
elsif value == nil
|
117
|
+
nil
|
118
|
+
else
|
119
|
+
raise "Colour value type '#{value.class}' not supported."
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
def e(seq)
|
126
|
+
if @output_type == :raw
|
127
|
+
"\\033[#{seq}m"
|
128
|
+
else
|
129
|
+
"\033[#{seq}m"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def colour_ansi(string, fg = nil, bg = nil)
|
134
|
+
unless fg == nil
|
135
|
+
colour_id = if fg.is_a? Unknown
|
136
|
+
fg.id
|
137
|
+
else
|
138
|
+
@palette.match_colour(fg)
|
139
|
+
end
|
140
|
+
string = e(colour_id + 30) + string
|
141
|
+
end
|
142
|
+
|
143
|
+
unless bg == nil
|
144
|
+
colour_id = if bg.is_a? Unknown
|
145
|
+
bg.id
|
146
|
+
else
|
147
|
+
@palette.match_colour(bg)
|
148
|
+
end
|
149
|
+
string = e(colour_id + 40) + string
|
150
|
+
end
|
151
|
+
|
152
|
+
string << e(0) unless (fg == nil) && (bg == nil)
|
153
|
+
|
154
|
+
string
|
155
|
+
end
|
156
|
+
|
157
|
+
def colour_extended(string, fg = nil, bg = nil)
|
158
|
+
unless fg == nil
|
159
|
+
colour_id = if fg.is_a? Unknown
|
160
|
+
fg.id
|
161
|
+
else
|
162
|
+
@palette.match_colour(fg)
|
163
|
+
end
|
164
|
+
string = e("38;5;#{colour_id}") + string
|
165
|
+
end
|
166
|
+
|
167
|
+
unless bg == nil
|
168
|
+
colour_id = if bg.is_a? Unknown
|
169
|
+
bg.id
|
170
|
+
else
|
171
|
+
@palette.match_colour(bg)
|
172
|
+
end
|
173
|
+
string = e("48;5;#{colour_id}") + string
|
174
|
+
end
|
175
|
+
|
176
|
+
string << e(0) unless (fg == nil) && (bg == nil)
|
177
|
+
|
178
|
+
string
|
179
|
+
end
|
180
|
+
|
181
|
+
def parse_colour_id(id_in_string)
|
182
|
+
id = String.new(id_in_string)
|
183
|
+
if id[0] == '@'
|
184
|
+
id[0] = ''
|
185
|
+
return id.to_i
|
186
|
+
end
|
187
|
+
|
188
|
+
raise "Invalid colour id #{id_in_string}."
|
189
|
+
end
|
190
|
+
|
191
|
+
def parse_rgb_value(rgb_value_in_string)
|
192
|
+
error_msg = "Invalid RGB value '#{rgb_value_in_string}'."
|
193
|
+
rgb_value = String.new rgb_value_in_string
|
194
|
+
if rgb_value[0] == '#'
|
195
|
+
rgb_value[0] = ''
|
196
|
+
elsif rgb_value[0..1] == '0x'
|
197
|
+
rgb_value[0..1] = ''
|
198
|
+
else
|
199
|
+
raise error_msg
|
200
|
+
end
|
201
|
+
|
202
|
+
raise error_msg unless rgb_value =~ /^[0-9a-fA-F]+$/
|
203
|
+
|
204
|
+
case rgb_value.length
|
205
|
+
when 3
|
206
|
+
rgb_value.scan(/./).map { |c| c.to_i 16 }
|
207
|
+
when 6
|
208
|
+
rgb_value.scan(/../).map { |c| c.to_i 16 }
|
209
|
+
else
|
210
|
+
raise error_msg
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def resolve_colour_name(name)
|
215
|
+
raise "Name '#{name}' not found." unless @names.key? name
|
216
|
+
|
217
|
+
@names[name]
|
218
|
+
end
|
219
|
+
|
220
|
+
def resolve_colour_def(colour_def)
|
221
|
+
return nil if colour_def == '' || colour_def == 'default'
|
222
|
+
|
223
|
+
begin
|
224
|
+
id = parse_colour_id colour_def
|
225
|
+
if @palette.is_known? id
|
226
|
+
Colour.new @palette.get_colour_value id
|
227
|
+
else
|
228
|
+
Unknown.new id
|
229
|
+
end
|
230
|
+
rescue RuntimeError
|
231
|
+
begin
|
232
|
+
Colour.new parse_rgb_value colour_def
|
233
|
+
rescue RuntimeError
|
234
|
+
begin
|
235
|
+
colour_def = resolve_colour_name colour_def
|
236
|
+
if colour_def.is_a? String
|
237
|
+
resolve_colour_def colour_def
|
238
|
+
else
|
239
|
+
colour_def
|
240
|
+
end
|
241
|
+
rescue RuntimeError
|
242
|
+
raise "Invalid colour definition '#{colour_def}'."
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|