mirror of
https://github.com/lihop/godot-xterm.git
synced 2024-09-20 16:36:19 +02:00
192 lines
6.2 KiB
GDScript3
192 lines
6.2 KiB
GDScript3
|
# 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
|