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