mirror of
https://github.com/lihop/godot-xterm.git
synced 2025-05-12 06:35:31 +02:00
Add/update more files
This commit is contained in:
parent
3307231b65
commit
0769592a1b
44 changed files with 4188 additions and 362 deletions
250
addons/godot_xterm/renderer/base_render_layer.gd
Normal file
250
addons/godot_xterm/renderer/base_render_layer.gd
Normal file
|
@ -0,0 +1,250 @@
|
|||
# Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const Constants = preload("res://addons/godot_xterm/buffer/constants.gd")
|
||||
const AttributeData = preload("res://addons/godot_xterm/buffer/attribute_data.gd")
|
||||
const CanvasRenderingContext2D = preload("res://addons/godot_xterm/renderer/canvas_rendering_context_2d.gd")
|
||||
|
||||
|
||||
const Attributes = Constants.Attributes
|
||||
# TODO: Something about these consts and atlas
|
||||
const INVERTED_DEFAULT_COLOR = Color(0, 0, 0, 1)
|
||||
const DEFAULT_COLOR = Color(1, 1, 1, 0)
|
||||
|
||||
var _container: Node
|
||||
var id: String
|
||||
var z_index: int
|
||||
var _alpha: bool
|
||||
var _colors
|
||||
var _renderer_id: int
|
||||
var _buffer_service
|
||||
var _options_service
|
||||
|
||||
var _ctx: CanvasRenderingContext2D
|
||||
var _scaled_char_width: int = 0
|
||||
var _scaled_char_height: int = 0
|
||||
var _scaled_cell_width: int = 0
|
||||
var _scaled_cell_height: int = 0
|
||||
var _scaled_char_left: int = 0
|
||||
var _scaled_char_top: int = 0
|
||||
var _char_atlas
|
||||
|
||||
|
||||
# An object that's reused when drawing glyphs in order to reduce GC.
|
||||
class GlyphIdentifier:
|
||||
extends Reference
|
||||
var chars = ''
|
||||
var code = 0
|
||||
var bg = 0
|
||||
var fg = 0
|
||||
var bold = false
|
||||
var dim = false
|
||||
var italic = false
|
||||
|
||||
var _current_glyph_identifier = GlyphIdentifier.new()
|
||||
|
||||
|
||||
func _init(container: Node, id: String, z_index: int, alpha: bool,
|
||||
colors: Dictionary, renderer_id: int, buffer_service, options_service):
|
||||
_container = container
|
||||
self.id = id
|
||||
self.z_index = z_index
|
||||
_alpha = alpha
|
||||
_colors = colors
|
||||
_renderer_id = renderer_id
|
||||
_buffer_service = buffer_service
|
||||
_options_service = options_service
|
||||
|
||||
_ctx = CanvasRenderingContext2D.new()
|
||||
_ctx.z_index = z_index
|
||||
_container.add_child(_ctx)
|
||||
|
||||
|
||||
|
||||
func on_grid_changed(start_row: int, end_row: int) -> void:
|
||||
pass
|
||||
|
||||
|
||||
func resize(dim) -> void:
|
||||
_scaled_cell_width = dim.scaled_cell_width
|
||||
_scaled_cell_height = dim.scaled_cell_height
|
||||
_scaled_char_width = dim.scaled_char_width
|
||||
_scaled_char_height = dim.scaled_char_height
|
||||
_scaled_char_left = dim.scaled_char_left
|
||||
_scaled_char_top = dim.scaled_char_top
|
||||
#_canvas_width = dim.scaled_canvas_width
|
||||
#_canvas_height = dim.scaled_canvas_height
|
||||
#this._canvas.style.width = `${dim.canvasWidth}px`;
|
||||
#this._canvas.style.height = `${dim.canvasHeight}px`;
|
||||
|
||||
func _fill_cells(x: int, y: int, width: int, height: int) -> void:
|
||||
_ctx.fill_rect(Rect2(x * _scaled_cell_width, y * _scaled_cell_height,
|
||||
width * _scaled_cell_width, height * _scaled_cell_height))
|
||||
|
||||
func _clear_cells(x: int, y: int, width: int, height: int) -> void:
|
||||
var scaled = Rect2(x * _scaled_cell_width, y * _scaled_cell_height,
|
||||
width * _scaled_cell_width, height * _scaled_cell_height)
|
||||
|
||||
if _alpha:
|
||||
_ctx.clear_rect(scaled)
|
||||
else:
|
||||
_ctx.fill_style = _colors.background
|
||||
_ctx.fill_rect(scaled)
|
||||
|
||||
|
||||
func _draw_chars(cell, x, y) -> void:
|
||||
# TODO
|
||||
#var contrast_color = _get_contrast_color(cell)
|
||||
var contrast_color = null
|
||||
|
||||
# skip cache right away if we draw in RGB
|
||||
# Note: to avoid bad runtime JoinedCellData will be skipped
|
||||
# in the cache handler itself (atlasDidDraw == false) and
|
||||
# fall through to uncached later down below
|
||||
if contrast_color or cell.is_fg_rgb() or cell.is_bg_rgb():
|
||||
_draw_uncached_chars(cell, x, y, contrast_color)
|
||||
return
|
||||
|
||||
var fg
|
||||
var bg
|
||||
if cell.is_inverse():
|
||||
fg = INVERTED_DEFAULT_COLOR if cell.is_bg_default() else cell.get_bg_color()
|
||||
bg = INVERTED_DEFAULT_COLOR if cell.is_fg_default() else cell.get_fg_color()
|
||||
else:
|
||||
bg = DEFAULT_COLOR if cell.is_bg_default() else cell.get_bg_color()
|
||||
fg = DEFAULT_COLOR if cell.is_fg_default() else cell.get_fg_color()
|
||||
|
||||
var draw_in_bright_color = _options_service.options.draw_bold_text_in_bright_colors and cell.is_bold() and fg < 8
|
||||
|
||||
fg = Color(fg as int + 8) if draw_in_bright_color else 0
|
||||
_current_glyph_identifier.chars = cell.get_chars() if cell.get_chars() else Constants.WHITESPACE_CELL_CHAR
|
||||
_current_glyph_identifier.code = cell.get_code() if cell.get_code() else Constants.WHITESPACE_CELL_CODE
|
||||
_current_glyph_identifier.bg = bg
|
||||
_current_glyph_identifier.fg = fg
|
||||
_current_glyph_identifier.bold = cell.is_bold() as bool
|
||||
_current_glyph_identifier.dim = cell.is_dim() as bool
|
||||
_current_glyph_identifier.italic = cell.is_italic() as bool
|
||||
var atlas_did_draw = _char_atlas and _char_atlas.draw(_ctx,
|
||||
_current_glyph_identifier, x * _scaled_cell_width + _scaled_char_left,
|
||||
y * _scaled_cell_width, _scaled_char_top)
|
||||
|
||||
if not atlas_did_draw:
|
||||
_draw_uncached_chars(cell, x, y)
|
||||
|
||||
|
||||
# Draws one or more charaters at one or more cells. The character(s) will be
|
||||
# clipped to ensure that they fit with the cell(s), including the cell to the
|
||||
# right if the last character is a wide character.
|
||||
func _draw_uncached_chars(cell, x: int, y: int, fg_override = null) -> void:
|
||||
_ctx.save()
|
||||
_ctx.font = _get_font(cell.is_bold() as bool, cell.is_italic() as bool)
|
||||
|
||||
if cell.is_inverse():
|
||||
if cell.is_bg_default():
|
||||
_ctx.fill_style = _colors.background
|
||||
elif cell.is_bg_rgb():
|
||||
_ctx.fill_style = AttributeData.to_color_rgb(cell.get_bg_color())
|
||||
else:
|
||||
var bg = cell.get_bg_color()
|
||||
if _options_service.options.draw_bold_text_in_bright_colors and cell.is_bold() and bg < 8:
|
||||
bg += 8
|
||||
_ctx.fill_style = _colors.ansi[bg]
|
||||
else:
|
||||
if cell.is_fg_default():
|
||||
_ctx.fill_style = _colors.foreground
|
||||
elif cell.is_fg_rgb():
|
||||
_ctx.fill_style = AttributeData.to_color_rgb(cell.get_fg_color())
|
||||
else:
|
||||
var fg = cell.get_fg_color()
|
||||
if _options_service.options.draw_bold_text_in_bright_colors and cell.is_bold() and fg < 8:
|
||||
fg += 8
|
||||
_ctx.fill_style = _colors.ansi[fg]
|
||||
|
||||
#_clip_row(y)
|
||||
|
||||
# Apply alpha to dim the character
|
||||
if cell.is_dim():
|
||||
pass
|
||||
#_ctx.global_alpha = DIM_OPACITY
|
||||
# Draw the character
|
||||
_ctx.fill_text(cell.get_chars(), x * _scaled_cell_width + _scaled_char_left,
|
||||
y * _scaled_cell_height + _scaled_char_top + _scaled_char_height / 2)
|
||||
_ctx.restore()
|
||||
|
||||
func _get_font(is_bold: bool, is_italic: bool) -> Font:
|
||||
var font_family = _options_service.options.font_family
|
||||
|
||||
if is_bold and is_italic and font_family.bold_italic:
|
||||
return font_family.bold_italic
|
||||
elif is_bold and font_family.bold:
|
||||
return font_family.bold
|
||||
elif is_italic and font_family.italic:
|
||||
return font_family.italic
|
||||
else:
|
||||
return font_family.regular
|
||||
|
||||
|
||||
func _get_contrast_color(cell):
|
||||
if _options_service.options.minimum_contrast_ratio == 1:
|
||||
return null
|
||||
|
||||
var adjusted_color = _colors.contrast_cache.get_color(cell.bg, cell.fg)
|
||||
if adjusted_color != null:
|
||||
return adjusted_color
|
||||
|
||||
var fg_color = cell.get_fg_color()
|
||||
var fg_color_mode = cell.get_fg_color_mode()
|
||||
var bg_color = cell.get_bg_color()
|
||||
var bg_color_mode = cell.get_bg_color_mode()
|
||||
var is_inverse = cell.is_inverse() as bool
|
||||
var is_bold = cell.is_bold() as bool
|
||||
if is_inverse:
|
||||
var temp = fg_color
|
||||
fg_color = bg_color
|
||||
bg_color = temp
|
||||
var temp2 = fg_color_mode
|
||||
fg_color_mode = bg_color_mode
|
||||
bg_color_mode = temp2
|
||||
|
||||
var bg_rgba = _resolve_background_rgba(bg_color_mode, bg_color, is_inverse)
|
||||
var fg_rgba = _resolve_foreground_rgba(fg_color_mode, fg_color, is_inverse, is_bold)
|
||||
# TODO
|
||||
#var result = rgba.ensure_contrast_ratio(bg_rgba, fg_rgba, _options_service.options.minimum_contrast_ratio)
|
||||
|
||||
func _resolve_background_rgba(bg_color_mode: int, bg_color: int, inverse: bool) -> int:
|
||||
match bg_color_mode:
|
||||
Attributes.CM_P16, Attributes.CM_P256:
|
||||
return _colors.ansi[bg_color].rgba
|
||||
Attributes.CM_RGB:
|
||||
return bg_color << 8
|
||||
Attributes.CM_DEFAULT, _:
|
||||
if inverse:
|
||||
return _colors.foreground.rgba
|
||||
else:
|
||||
return _colors.background.rgba
|
||||
|
||||
|
||||
func _resolve_foreground_rgba(fg_color_mode: int, fg_color: int, inverse: bool, bold: bool):
|
||||
match fg_color_mode:
|
||||
Attributes.CM_P16, Attributes.CM_P256:
|
||||
if _options_service.options.draw_bold_text_in_bright_colors and bold and fg_color < 8:
|
||||
return _colors.ansi[fg_color].rgba
|
||||
Attributes.CM_RGB:
|
||||
return fg_color << 8
|
||||
Attributes.CM_DEFAULT, _:
|
||||
if inverse:
|
||||
return _colors.background.rgba
|
||||
else:
|
||||
return _colors.foreground.rgba
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
74
addons/godot_xterm/renderer/canvas_rendering_context_2d.gd
Normal file
74
addons/godot_xterm/renderer/canvas_rendering_context_2d.gd
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Copyright (c) 2020 The GodotXterm authors. All rights reserved.
|
||||
# License MIT
|
||||
extends Node2D
|
||||
class_name CanvasRenderingContext2D
|
||||
# This is a shim for the CavasRenderingContext2D interface of HTML5's Canvas API,
|
||||
# which the xterm.js renderer code uses heavily. It extends Node2D to take
|
||||
# advantage of the z_index property and also uses many methods of CanvasItem
|
||||
# which Node2D inherits.
|
||||
|
||||
|
||||
var fill_style
|
||||
var font = preload("res://addons/godot_xterm/fonts/source_code_pro/source_code_pro_regular.tres")
|
||||
var _saved
|
||||
var _draw_buffer = []
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
pass # Replace with function body.
|
||||
|
||||
|
||||
func draw_rect_deferred(rect: Rect2, color: Color):
|
||||
_draw_buffer.append({"method": "draw_rect", "args": [rect, color]})
|
||||
update()
|
||||
|
||||
|
||||
func clear_rect(rect: Rect2):
|
||||
draw_rect_deferred(rect, Color(0, 0, 0, 0))
|
||||
|
||||
|
||||
func fill_rect(rect: Rect2):
|
||||
draw_rect_deferred(rect, fill_style)
|
||||
|
||||
|
||||
func fill_text(text: String, x: int, y: int):
|
||||
_draw_buffer.append({"method": "_draw_text", "args": [font, Vector2(x, y), text, fill_style]})
|
||||
update()
|
||||
|
||||
func _draw_text(font: Font, pos: Vector2, text: String, color) -> void:
|
||||
for i in text.length():
|
||||
var c = text[i]
|
||||
var next_char = text[i + 1] if i + 1 < text.length() else ''
|
||||
var advance = draw_char(font, pos, c, next_char, color)
|
||||
pos.x += advance
|
||||
|
||||
|
||||
func _draw():
|
||||
for command in _draw_buffer:
|
||||
self.callv(command.method, command.args)
|
||||
_draw_buffer.resize(0)
|
||||
|
||||
|
||||
func save():
|
||||
_saved = {
|
||||
'fill_style': fill_style,
|
||||
'font': font,
|
||||
}
|
||||
|
||||
|
||||
func restore():
|
||||
fill_style = _saved['fill_style']
|
||||
font = _saved['font']
|
||||
|
||||
|
||||
func measure_text(text: String):
|
||||
var text_metrics = TextMetrics.new()
|
||||
text_metrics.width = font.get_string_size(text).x
|
||||
return text_metrics
|
||||
|
||||
class TextMetrics:
|
||||
extends Reference
|
||||
# https://developer.mozilla.org/en-US/docs/Web/API/TextMetrics
|
||||
|
||||
var width
|
46
addons/godot_xterm/renderer/character_joiner_registry.gd
Normal file
46
addons/godot_xterm/renderer/character_joiner_registry.gd
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const CellData = preload("res://addons/godot_xterm/buffer/cell_data.gd")
|
||||
|
||||
|
||||
class JoinedCellData extends "res://addons/godot_xterm/buffer/attribute_data.gd":
|
||||
|
||||
|
||||
var _width: int = 0
|
||||
var content: int = 0
|
||||
var combined_data: String = ''
|
||||
|
||||
|
||||
func _init(first_cell, chars: String, width: int):
|
||||
fg = first_cell.fg
|
||||
bg = first_cell.bg
|
||||
combined_data = chars
|
||||
_width = width
|
||||
|
||||
|
||||
var _character_joiners: Array = []
|
||||
var _next_character_joiner_id = 0
|
||||
var _work_cell = CellData.new()
|
||||
var _buffer_service
|
||||
|
||||
|
||||
func _init(buffer_service):
|
||||
_buffer_service = buffer_service
|
||||
|
||||
|
||||
func get_joined_characters(row: int) -> Array:
|
||||
if _character_joiners.empty():
|
||||
return []
|
||||
|
||||
var line = _buffer_service.buffer.lines.get_el(row)
|
||||
if not line or line.length == 0:
|
||||
return []
|
||||
|
||||
var ranges = []
|
||||
var line_str = line.translate_to_string(true)
|
||||
|
||||
return ranges
|
118
addons/godot_xterm/renderer/renderer.gd
Normal file
118
addons/godot_xterm/renderer/renderer.gd
Normal file
|
@ -0,0 +1,118 @@
|
|||
# Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends Reference
|
||||
|
||||
|
||||
const CharacterJoinerRegistry = preload("res://addons/godot_xterm/renderer/character_joiner_registry.gd")
|
||||
const TextRenderLayer = preload("res://addons/godot_xterm/renderer/text_render_layer.gd")
|
||||
|
||||
signal redraw_requested
|
||||
signal options_changed
|
||||
signal grid_changed(start, end)
|
||||
|
||||
var _id: int
|
||||
var _render_layers: Array
|
||||
var _device_pixel_ratio: float
|
||||
var _character_joiner_registry
|
||||
var _colors
|
||||
var _container
|
||||
var _buffer_service
|
||||
var _options_service
|
||||
var _char_size_service
|
||||
|
||||
var dimensions
|
||||
|
||||
|
||||
func _init(colors, container: Node, buffer_service, options_service):
|
||||
_id = get_instance_id()
|
||||
_colors = colors
|
||||
_container = container
|
||||
_buffer_service = buffer_service
|
||||
_options_service = options_service
|
||||
|
||||
var allow_transparency = _options_service.options.allow_transparency
|
||||
_character_joiner_registry = CharacterJoinerRegistry.new(_buffer_service)
|
||||
|
||||
_render_layers = [
|
||||
TextRenderLayer.new(_container, 0, _colors, _character_joiner_registry,
|
||||
allow_transparency, _id, _buffer_service, _options_service)
|
||||
]
|
||||
|
||||
# Connect render layers to our signals.
|
||||
for layer in _render_layers:
|
||||
self.connect("options_changed", layer, "on_options_changed")
|
||||
self.connect("grid_changed", layer, "on_grid_changed")
|
||||
|
||||
dimensions = {
|
||||
"scaled_char_width": 0,
|
||||
"scaled_char_height": 0,
|
||||
"scaled_cell_width": 0,
|
||||
"scaled_cell_height": 0,
|
||||
"scaled_char_left": 0,
|
||||
"scaled_char_top": 0,
|
||||
"scaled_canvas_width": 0,
|
||||
"scaled_canvas_height": 0,
|
||||
"canvas_width": 0,
|
||||
"canvas_height": 0,
|
||||
"actual_cell_width": 0,
|
||||
"actual_cell_height": 0,
|
||||
}
|
||||
_device_pixel_ratio = OS.get_screen_dpi()
|
||||
_update_dimensions()
|
||||
emit_signal("options_changed")
|
||||
|
||||
|
||||
func on_resize(cols, rows):
|
||||
# Update character and canvas dimensions
|
||||
_update_dimensions()
|
||||
|
||||
# Resize all render layers
|
||||
for layer in _render_layers:
|
||||
layer.resize(dimensions)
|
||||
|
||||
|
||||
func refresh_rows(start: int, end: int) -> void:
|
||||
emit_signal("grid_changed", start, end)
|
||||
|
||||
|
||||
# Recalculates the character and canvas dimensions.
|
||||
func _update_dimensions():
|
||||
var char_width = 0
|
||||
var char_height = 0
|
||||
|
||||
for font in _options_service.options.font_family.values():
|
||||
var size = font.get_string_size("W")
|
||||
char_width = max(char_width, size.x)
|
||||
char_height = max(char_height, size.y)
|
||||
|
||||
dimensions.scaled_char_width = char_width
|
||||
dimensions.scaled_char_height = char_height
|
||||
|
||||
# Calculate the scaled cell height, if line_height is not 1 then the value
|
||||
# will be floored because since line_height can never be lower then 1, there
|
||||
# is a guarantee that the scaled line height will always be larger than
|
||||
# scaled char height.
|
||||
dimensions.scaled_cell_height = floor(dimensions.scaled_char_height * _options_service.options.line_height)
|
||||
|
||||
# Calculate the y coordinate within a cell that text should draw from in
|
||||
# order to draw in the center of a cell.
|
||||
dimensions.scaled_char_top = 0 if _options_service.options.line_height == 1 else \
|
||||
round((dimensions.scaled_cell_height - dimensions.scaled_char_height) / 2)
|
||||
|
||||
# Calculate the scaled cell width, taking the letter_spacing into account.
|
||||
dimensions.scaled_cell_width = dimensions.scaled_char_width + round(_options_service.options.letter_spacing)
|
||||
|
||||
# Calculate the x coordinate with a cell that text should draw from in
|
||||
# order to draw in the center of a cell.
|
||||
dimensions.scaled_char_left = floor(_options_service.options.letter_spacing / 2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
191
addons/godot_xterm/renderer/text_render_layer.gd
Normal file
191
addons/godot_xterm/renderer/text_render_layer.gd
Normal file
|
@ -0,0 +1,191 @@
|
|||
# Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
# Ported to GDScript by the GodotXterm authors.
|
||||
# License MIT
|
||||
extends "res://addons/godot_xterm/renderer/base_render_layer.gd"
|
||||
|
||||
|
||||
const CellData = preload("res://addons/godot_xterm/buffer/cell_data.gd")
|
||||
const CharacterJoinerRegistry = preload("res://addons/godot_xterm/renderer/character_joiner_registry.gd")
|
||||
const JoinedCellData = CharacterJoinerRegistry.JoinedCellData
|
||||
const Content = Constants.Content
|
||||
|
||||
var _state
|
||||
var _character_width: int = 0
|
||||
var _character_font: DynamicFont = preload("res://addons/godot_xterm/fonts/source_code_pro/source_code_pro_regular.tres")
|
||||
var _character_overlap_cache: Dictionary = {}
|
||||
var _character_joiner_registry
|
||||
var _work_cell = CellData.new()
|
||||
|
||||
|
||||
func _init(container: Node, z_index: int, colors, character_joiner_registry,
|
||||
alpha: bool, renderer_id: int, buffer_service, options_service).(container,
|
||||
'text', z_index, alpha, colors, renderer_id, buffer_service, options_service):
|
||||
_state = null #TODO what?
|
||||
_character_joiner_registry = character_joiner_registry
|
||||
|
||||
|
||||
func on_grid_changed(first_row: int, last_row: int) -> void:
|
||||
_clear_cells(0, first_row, _buffer_service.cols, last_row - first_row + 1)
|
||||
_draw_background(first_row, last_row)
|
||||
_draw_foreground(first_row, last_row)
|
||||
|
||||
# Finally draw everything that has been queued in the draw buffer.
|
||||
_ctx.update()
|
||||
|
||||
|
||||
func on_options_changed() -> void:
|
||||
pass
|
||||
|
||||
|
||||
func _cells(first_row: int, last_row: int, joiner_registry = null) -> Array:
|
||||
var cells = []
|
||||
|
||||
for y in range(first_row, last_row + 1):
|
||||
var row = y + _buffer_service.buffer.ydisp
|
||||
var line = _buffer_service.buffer.lines.get_el(row)
|
||||
var joined_ranges = joiner_registry.get_joined_characters(row) if joiner_registry else []
|
||||
for x in range(_buffer_service.cols):
|
||||
line.load_cell(x, _work_cell)
|
||||
var cell = _work_cell
|
||||
|
||||
# If true, indicates that the current character(s) to draw were joined.
|
||||
var is_joined = false
|
||||
var last_char_x = x
|
||||
|
||||
# The character to the left is a wide character, drawing is owned by
|
||||
# the char at x-1
|
||||
if cell.get_width() == 0:
|
||||
continue
|
||||
|
||||
# Process any joined character range as needed. Because of how the
|
||||
# ranges are produced, we know that they are valid for the characters
|
||||
# and attributes of our input.
|
||||
if not joined_ranges.empty() and x == joined_ranges[0][0]:
|
||||
is_joined = true
|
||||
var r = joined_ranges.pop_front()
|
||||
|
||||
# We already know the exact start and end column of the joined
|
||||
# range, so we get the string and width representing it directly
|
||||
|
||||
cell = JoinedCellData.new(_work_cell,
|
||||
line.trans_late_to_string(true, r[0], r[1]), r[1] - r[0])
|
||||
|
||||
# Skip over the cells occupied by this range in the loop
|
||||
last_char_x = r[1] - 1
|
||||
|
||||
# If the character is an overlapping char and the character to the
|
||||
# right is a space, take ownership of the cell to the right. We skip
|
||||
# this check for joined characters because their rendering likely won't
|
||||
# yield the same result as rendering the last character individually.
|
||||
if not is_joined and _is_overlapping(cell):
|
||||
if last_char_x < line.length - 1 and line.get_codepoint(last_char_x + 1) == Constants.NULL_CELL_CODE:
|
||||
# patch width to 2
|
||||
cell.content &= ~Content.WIDTH_MASK
|
||||
cell.content |= 2 << Content.WIDTH_SHIFT
|
||||
|
||||
# Append a new instance of cell, as we wil reuse the current instance.
|
||||
cells.append({"cell": CellData.from_char_data(cell.get_as_char_data()),
|
||||
"x": x, "y": y})
|
||||
|
||||
x = last_char_x
|
||||
|
||||
return cells
|
||||
|
||||
|
||||
func _draw_background(first_row: int, last_row: int) -> void:
|
||||
var ctx = _ctx
|
||||
var cols = _buffer_service.cols
|
||||
var start_x = 0
|
||||
var start_y = 0
|
||||
var prev_fill_style = null
|
||||
|
||||
ctx.save()
|
||||
|
||||
for c in _cells(first_row, last_row, null):
|
||||
var cell = c.cell
|
||||
var x = c.x
|
||||
var y = c.y
|
||||
|
||||
# libvte and xterm draw the background (but not foreground) of invisible characters,
|
||||
# so we should too.
|
||||
var next_fill_style = null # null represents the default background color
|
||||
|
||||
if cell.is_inverse():
|
||||
if cell.is_fg_default():
|
||||
next_fill_style = _colors.foreground
|
||||
elif cell.is_fg_rgb():
|
||||
next_fill_style = cell.get_fg_color() # TODO: Figure out how to convert this to Color()
|
||||
else:
|
||||
next_fill_style = _colors.ansi[cell.get_fg_color()]
|
||||
elif cell.is_bg_rgb():
|
||||
next_fill_style = cell.get_bg_color() # TODO: Figure out how to convert this to Color()
|
||||
elif cell.is_bg_palette():
|
||||
next_fill_style = _colors.ansi[cell.get_bg_color()]
|
||||
|
||||
if prev_fill_style == null:
|
||||
# This is either the first iteration, or the default background was set. Either way, we
|
||||
# don't need to draw anything.
|
||||
start_x = x
|
||||
start_y = y
|
||||
|
||||
if y != start_y:
|
||||
# our row changed, draw the previous row
|
||||
ctx.fill_style = prev_fill_style if prev_fill_style else Color()
|
||||
_fill_cells(start_x, start_y, cols - start_x, 1)
|
||||
start_x = x
|
||||
start_y = y
|
||||
elif prev_fill_style != next_fill_style:
|
||||
# our color changed, draw the previous characters in this row
|
||||
ctx.fill_style = prev_fill_style if prev_fill_style else Color()
|
||||
start_x = x
|
||||
start_y = y
|
||||
|
||||
prev_fill_style = next_fill_style
|
||||
|
||||
# flush the last color we encountered
|
||||
if prev_fill_style != null:
|
||||
ctx.fill_style = prev_fill_style
|
||||
_fill_cells(start_x, start_y, cols - start_x, 1)
|
||||
|
||||
ctx.restore()
|
||||
|
||||
|
||||
func _draw_foreground(first_row: int, last_row: int) -> void:
|
||||
for c in _cells(first_row, last_row, _character_joiner_registry):
|
||||
var cell = c.cell
|
||||
var x = c.x
|
||||
var y = c.y
|
||||
|
||||
if cell.is_invisible():
|
||||
return
|
||||
|
||||
_draw_chars(cell, x, y)
|
||||
|
||||
|
||||
func _is_overlapping(cell) -> bool:
|
||||
# Only single cell characters can be overlapping, rendering issues can
|
||||
# occur without this check
|
||||
if cell.get_width() != 1:
|
||||
return false
|
||||
|
||||
var chars = cell.get_chars()
|
||||
|
||||
# Deliver from cache if available
|
||||
if _character_overlap_cache.has(chars):
|
||||
return _character_overlap_cache[chars]
|
||||
|
||||
# Setup the font
|
||||
_ctx.save()
|
||||
_ctx.font = _character_font
|
||||
|
||||
# Measure the width of the character, but floor it
|
||||
# because that is what the renderer does when it calculates
|
||||
# the character dimensions wer are comparing against
|
||||
var overlaps = floor(_ctx.measure_text(chars).width) > _character_width
|
||||
|
||||
# Restore the original context
|
||||
_ctx.restore()
|
||||
|
||||
# Cache and return
|
||||
_character_overlap_cache[chars] = overlaps
|
||||
return overlaps
|
Loading…
Add table
Add a link
Reference in a new issue