This commit is contained in:
Face
2025-07-25 18:55:28 +03:00
parent 558bb2fc1b
commit 1576ef1465
10 changed files with 271 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
<svg id="resizeHandle" width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<!-- Chrome-style resize handle with two diagonal lines -->
<line x1="20" y1="32" x2="32" y2="20" stroke="#666" stroke-width="3" stroke-linecap="round"/>
<line x1="28" y1="32" x2="32" y2="28" stroke="#666" stroke-width="3" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 384 B

View File

@@ -0,0 +1,37 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://tb7e8bcs8kqv"
path="res://.godot/imported/resize-handle.svg-433a7f0ff4889b64a8919f3af66061e6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://Assets/Icons/resize-handle.svg"
dest_files=["res://.godot/imported/resize-handle.svg-433a7f0ff4889b64a8919f3af66061e6.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -1,4 +1,4 @@
[gd_resource type="Theme" load_steps=44 format=3 uid="uid://bn6rbmdy60lhr"]
[gd_resource type="Theme" load_steps=47 format=3 uid="uid://bn6rbmdy60lhr"]
[ext_resource type="Texture2D" uid="uid://dn4dxn8hkrd64" path="res://Assets/Icons/checkbox.svg" id="1_75mhk"]
[ext_resource type="Texture2D" uid="uid://b6ucuyluuw43" path="res://Assets/Icons/checkbox_pressed_grayscale.svg" id="2_2abar"]
@@ -184,6 +184,45 @@ corner_radius_top_right = 4
corner_radius_bottom_right = 4
corner_radius_bottom_left = 4
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_n33pi"]
content_margin_left = 5.0
bg_color = Color(0.6, 0.6, 0.6, 0)
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color(0, 0, 0, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rmpy5"]
content_margin_left = 5.0
bg_color = Color(0.6, 0.6, 0.6, 0)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0, 0, 0, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_8uupr"]
content_margin_left = 5.0
bg_color = Color(0.6, 0.6, 0.6, 0)
border_width_left = 1
border_width_top = 1
border_width_right = 1
border_width_bottom = 1
border_color = Color(0, 0, 0, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[resource]
Button/styles/focus = SubResource("StyleBoxEmpty_c32on")
Button/styles/hover = SubResource("StyleBoxFlat_0v877")
@@ -248,3 +287,12 @@ RichTextLabel/styles/focus = SubResource("StyleBoxEmpty_jecr6")
TabContainer/styles/tab_hovered = SubResource("StyleBoxFlat_1vd2l")
TabContainer/styles/tab_selected = SubResource("StyleBoxFlat_4akvr")
TabContainer/styles/tab_unselected = SubResource("StyleBoxFlat_r011l")
TextEdit/colors/caret_color = Color(0, 0, 0, 1)
TextEdit/colors/font_color = Color(0, 0, 0, 1)
TextEdit/colors/font_placeholder_color = Color(0, 0, 0, 0.6)
TextEdit/colors/font_readonly_color = Color(0.196078, 0.196078, 0.196078, 1)
TextEdit/colors/font_selected_color = Color(1, 1, 1, 1)
TextEdit/colors/selection_color = Color(0.313726, 0.403922, 0.8, 1)
TextEdit/styles/focus = SubResource("StyleBoxFlat_n33pi")
TextEdit/styles/normal = SubResource("StyleBoxFlat_rmpy5")
TextEdit/styles/read_only = SubResource("StyleBoxFlat_8uupr")

27
Scenes/Tags/textarea.tscn Normal file
View File

@@ -0,0 +1,27 @@
[gd_scene load_steps=4 format=3 uid="uid://kfm80txc5t8m"]
[ext_resource type="Script" uid="uid://bmcx7mr4nye6a" path="res://Scripts/Tags/textarea.gd" id="1_textarea"]
[ext_resource type="Theme" uid="uid://bn6rbmdy60lhr" path="res://Scenes/Styles/BrowserText.tres" id="2_theme"]
[ext_resource type="Script" uid="uid://c5xpoyqcg1p8k" path="res://Scripts/ResizableTextEdit.gd" id="3_kbdk1"]
[node name="Textarea" type="Control"]
layout_mode = 3
anchors_preset = 10
anchor_right = 1.0
grow_horizontal = 2
script = ExtResource("1_textarea")
[node name="TextEdit" type="TextEdit" parent="."]
layout_mode = 1
offset_right = 341.0
offset_bottom = 128.0
size_flags_horizontal = 3
size_flags_vertical = 3
theme = ExtResource("2_theme")
placeholder_text = "Enter text here..."
context_menu_enabled = false
emoji_menu_enabled = false
middle_mouse_paste_enabled = false
wrap_mode = 1
caret_blink = true
script = ExtResource("3_kbdk1")

View File

@@ -0,0 +1,64 @@
extends TextEdit
@onready var resize_handle = TextureRect.new()
var is_resizing = false
var resize_start_pos = Vector2()
var original_size = Vector2()
var min_size = Vector2(100, 50)
func _ready():
# Create resize handle as TextureRect child of TextEdit
resize_handle.texture = load("res://Assets/Icons/resize-handle.svg")
resize_handle.size = Vector2(32, 32)
resize_handle.mouse_default_cursor_shape = Control.CURSOR_FDIAGSIZE
resize_handle.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
add_child(resize_handle)
# Position handle in bottom-right corner
_update_handle_position()
# Connect signals
resize_handle.gui_input.connect(_on_resize_handle_input)
resized.connect(_update_handle_position)
func _gui_input(event):
if event is InputEventMouseButton and get_global_rect().has_point(get_viewport().get_mouse_position()):
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
set_v_scroll(get_v_scroll() - 2)
accept_event()
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
set_v_scroll(get_v_scroll() + 2)
accept_event()
func _update_handle_position():
if resize_handle:
resize_handle.position = Vector2(size.x - 32, size.y - 32)
func _on_resize_handle_input(event):
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
is_resizing = true
resize_start_pos = event.global_position
original_size = size
else:
is_resizing = false
elif event is InputEventMouseMotion and is_resizing:
var delta = event.global_position - resize_start_pos
var new_size = original_size + delta
new_size.x = max(new_size.x, min_size.x)
new_size.y = max(new_size.y, min_size.y)
size = new_size
# Sync parent Control size
var parent_control = get_parent() as Control
if parent_control:
parent_control.size = new_size
parent_control.custom_minimum_size = new_size
if parent_control:
parent_control.size = new_size
parent_control.custom_minimum_size = new_size

View File

@@ -0,0 +1 @@
uid://c5xpoyqcg1p8k

View File

@@ -35,5 +35,4 @@ func init(element: HTMLParser.HTMLElement) -> void:
if selected_index >= 0:
option_button.selected = selected_index
add_child(option_button)
custom_minimum_size = option_button.size

75
Scripts/Tags/textarea.gd Normal file
View File

@@ -0,0 +1,75 @@
extends Control
const BROWSER_TEXT = preload("res://Scenes/Styles/BrowserText.tres")
func init(element: HTMLParser.HTMLElement) -> void:
var text_edit: TextEdit = $TextEdit
var placeholder = element.get_attribute("placeholder")
var value = element.get_attribute("value")
var rows = element.get_attribute("rows")
var cols = element.get_attribute("cols")
var maxlength = element.get_attribute("maxlength")
var readonly = element.get_attribute("readonly")
var disabled = element.get_attribute("disabled")
# Set placeholder text
text_edit.placeholder_text = placeholder
# Set initial value
if value.length() > 0:
text_edit.text = value
elif element.text_content.length() > 0:
text_edit.text = element.text_content
# We assume to fit $rows amount of new lines
var line_height = text_edit.get_theme_default_font().get_height(text_edit.get_theme_default_font_size())
# We assume the biggest letter typed is "M" (77), and optimize to fit $cols amount of "M"
var char_width = text_edit.get_theme_default_font().get_char_size(77, text_edit.get_theme_default_font_size()).x
var min_height = line_height * (rows.to_int() if rows.length() > 0 else 4) + 26 # padding
var min_width = char_width * (cols.to_int() if cols.length() > 0 else 50) + 16 # padding
text_edit.custom_minimum_size = Vector2(min_width, min_height)
text_edit.size = Vector2(min_width, min_height)
text_edit.min_size = Vector2(min_width, min_height)
# Sync Control size with TextEdit
custom_minimum_size = text_edit.custom_minimum_size
size = text_edit.size
# Set readonly state
if readonly.length() > 0:
text_edit.editable = false
# Set disabled state
if disabled.length() > 0:
text_edit.editable = false
var stylebox = StyleBoxFlat.new()
stylebox.bg_color = Color(0.8, 0.8, 0.8, 1.0)
stylebox.border_color = Color(0, 0, 0, 1.0)
stylebox.border_width_bottom = 1
stylebox.border_width_top = 1
stylebox.border_width_left = 1
stylebox.border_width_right = 1
stylebox.corner_radius_bottom_left = 3
stylebox.corner_radius_bottom_right = 3
stylebox.corner_radius_top_left = 3
stylebox.corner_radius_top_right = 3
text_edit.add_theme_stylebox_override("normal", stylebox)
text_edit.add_theme_stylebox_override("focus", stylebox)
text_edit.add_theme_stylebox_override("readonly", stylebox)
# Handle maxlength
if maxlength.length() > 0 and maxlength.is_valid_int():
var max_len = maxlength.to_int()
text_edit.text_changed.connect(_on_text_changed.bind(max_len))
func _on_text_changed(max_length: int) -> void:
var text_edit = $TextEdit as TextEdit
if text_edit.text.length() > max_length:
var cursor_pos = text_edit.get_caret_column()
var line_pos = text_edit.get_caret_line()
text_edit.text = text_edit.text.substr(0, max_length)
text_edit.set_caret_line(line_pos)
text_edit.set_caret_column(min(cursor_pos, text_edit.get_line(line_pos).length()))

View File

@@ -0,0 +1 @@
uid://bmcx7mr4nye6a

View File

@@ -25,6 +25,7 @@ const OL = preload("res://Scenes/Tags/ol.tscn")
const LI = preload("res://Scenes/Tags/li.tscn")
const SELECT = preload("res://Scenes/Tags/select.tscn")
const OPTION = preload("res://Scenes/Tags/option.tscn")
const TEXTAREA = preload("res://Scenes/Tags/textarea.tscn")
const MIN_SIZE = Vector2i(750, 200)
@@ -85,6 +86,14 @@ line breaks
<option value=\"test5\">Test 5</option>
</select>
<textarea />
<textarea cols=\"30\" />
<textarea rows=\"2\" />
<textarea maxlength=\"20\" />
<textarea readonly=\"true\">le skibidi le toilet</textarea>
<textarea disabled=\"true\" value=\"DISABLED\" />
<textarea placeholder=\"this is a placeholder...\" />
<!-- action, method, and type=submit are for when we implement Lua -->
<form action=\"/submit\" method=\"POST\">
<span>Name:</span>
@@ -479,6 +488,9 @@ func create_element_node(element: HTMLParser.HTMLElement) -> Control:
"option":
node = OPTION.instantiate()
node.init(element)
"textarea":
node = TEXTAREA.instantiate()
node.init(element)
_:
return null