diff options
author | Gone2Daly <71726742+Gone2Daly@users.noreply.github.com> | 2023-07-22 21:05:42 +0200 |
---|---|---|
committer | Gone2Daly <71726742+Gone2Daly@users.noreply.github.com> | 2023-07-22 21:05:42 +0200 |
commit | 71b3cd829f80de4c2cd3972d8bfd5ee470a5d180 (patch) | |
tree | b4280fde6eef2ae6987648bc7bf8e00e9011bb7f /game/addons/zylann.hterrain/tools/util | |
parent | ce9022d0df74d6c33db3686622be2050d873ab0b (diff) |
init_testtest3d
Diffstat (limited to 'game/addons/zylann.hterrain/tools/util')
10 files changed, 837 insertions, 0 deletions
diff --git a/game/addons/zylann.hterrain/tools/util/dialog_fitter.gd b/game/addons/zylann.hterrain/tools/util/dialog_fitter.gd new file mode 100644 index 0000000..59bb194 --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/dialog_fitter.gd @@ -0,0 +1,53 @@ + +# If you make a container-based UI inside a WindowDialog, there is a chance it will overflow +# because WindowDialogs don't adjust by themselves. This happens when the user has a different +# font size than yours, and can cause controls to be unusable (like buttons at the bottom). +# This script adjusts the size of the parent WindowDialog based on the first Container it finds +# when the node becomes visible. + +@tool +# Needs to be a Control, otherwise we don't receive the notification... +extends Control + +const HT_Util = preload("../../util/util.gd") + + +func _notification(what: int): + if HT_Util.is_in_edited_scene(self): + return + if is_inside_tree() and what == Control.NOTIFICATION_VISIBILITY_CHANGED: + #print("Visible ", is_visible_in_tree(), ", ", visible) + call_deferred("_fit_to_contents") + + +func _fit_to_contents(): + var dialog : Window = get_parent() + for child in dialog.get_children(): + if child is Container: + var child_rect : Rect2 = child.get_global_rect() + var dialog_rect := Rect2(Vector2(), dialog.size) + #print("Dialog: ", dialog_rect, ", contents: ", child_rect, " ", child.get_path()) + if not dialog_rect.encloses(child_rect): + var margin : Vector2 = child.get_rect().position + #print("Fitting ", dialog.get_path(), " from ", dialog.rect_size, + # " to ", child_rect.size + margin * 2.0) + dialog.min_size = child_rect.size + margin * 2.0 + + +#func _process(delta): +# update() + +# DEBUG +#func _draw(): +# var self_global_pos = get_global_rect().position +# +# var dialog : Control = get_parent() +# var dialog_rect := dialog.get_global_rect() +# dialog_rect.position -= self_global_pos +# draw_rect(dialog_rect, Color(1,1,0), false) +# +# for child in dialog.get_children(): +# if child is Container: +# var child_rect : Rect2 = child.get_global_rect() +# child_rect.position -= self_global_pos +# draw_rect(child_rect, Color(1,1,0,0.1)) diff --git a/game/addons/zylann.hterrain/tools/util/dialog_fitter.tscn b/game/addons/zylann.hterrain/tools/util/dialog_fitter.tscn new file mode 100644 index 0000000..2e3b00c --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/dialog_fitter.tscn @@ -0,0 +1,10 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/zylann.hterrain/tools/util/dialog_fitter.gd" type="Script" id=1] + +[node name="DialogFitter" type="Control"] +mouse_filter = 2 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} diff --git a/game/addons/zylann.hterrain/tools/util/editor_util.gd b/game/addons/zylann.hterrain/tools/util/editor_util.gd new file mode 100644 index 0000000..a6d9eff --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/editor_util.gd @@ -0,0 +1,104 @@ + +# Editor-specific utilities. +# This script cannot be loaded in an exported game. + +@tool + + +# This is normally an `EditorFileDialog`. I can't type-hint this one properly, +# because when I test UI in isolation, I can't use `EditorFileDialog`. +static func create_open_file_dialog() -> ConfirmationDialog: + var d + if Engine.is_editor_hint(): + # TODO Workaround bug when editor-only classes are created in source code, even if not run + # https://github.com/godotengine/godot/issues/73525 +# d = EditorFileDialog.new() + d = ClassDB.instantiate(&"EditorFileDialog") + d.file_mode = EditorFileDialog.FILE_MODE_OPEN_FILE + d.access = EditorFileDialog.ACCESS_RESOURCES + else: + d = FileDialog.new() + d.file_mode = FileDialog.FILE_MODE_OPEN_FILE + d.access = FileDialog.ACCESS_RESOURCES + d.unresizable = false + return d + + +static func create_open_dir_dialog() -> ConfirmationDialog: + var d + if Engine.is_editor_hint(): + # TODO Workaround bug when editor-only classes are created in source code, even if not run + # https://github.com/godotengine/godot/issues/73525 +# d = EditorFileDialog.new() + d = ClassDB.instantiate(&"EditorFileDialog") + d.file_mode = EditorFileDialog.FILE_MODE_OPEN_DIR + d.access = EditorFileDialog.ACCESS_RESOURCES + else: + d = FileDialog.new() + d.file_mode = FileDialog.FILE_MODE_OPEN_DIR + d.access = FileDialog.ACCESS_RESOURCES + d.unresizable = false + return d + + +# If you want to open using Image.load() +static func create_open_image_dialog() -> ConfirmationDialog: + var d = create_open_file_dialog() + _add_image_filters(d) + return d + + +# If you want to open using load(), +# although it might still fail if the file is imported as Image... +static func create_open_texture_dialog() -> ConfirmationDialog: + var d = create_open_file_dialog() + _add_texture_filters(d) + return d + + +static func create_open_texture_array_dialog() -> ConfirmationDialog: + var d = create_open_file_dialog() + _add_texture_array_filters(d) + return d + +# TODO Post a proposal, we need a file dialog filtering on resource types, not on file extensions! + +static func _add_image_filters(file_dialog): + file_dialog.add_filter("*.png ; PNG files") + file_dialog.add_filter("*.jpg ; JPG files") + #file_dialog.add_filter("*.exr ; EXR files") + + +static func _add_texture_filters(file_dialog): + _add_image_filters(file_dialog) + # Godot + file_dialog.add_filter("*.ctex ; CompressedTexture files") + # Packed textures + file_dialog.add_filter("*.packed_tex ; HTerrainPackedTexture files") + + +static func _add_texture_array_filters(file_dialog): + _add_image_filters(file_dialog) + # Godot + file_dialog.add_filter("*.ctexarray ; TextureArray files") + # Packed textures + file_dialog.add_filter("*.packed_texarr ; HTerrainPackedTextureArray files") + + +# Tries to load a texture with the ResourceLoader, and if it fails, attempts +# to load it manually as an ImageTexture +static func load_texture(path: String, logger) -> Texture: + var tex : Texture = load(path) + if tex != null: + return tex + # This can unfortunately happen when the editor didn't import assets yet. + # See https://github.com/godotengine/godot/issues/17483 + logger.error(str("Failed to load texture ", path, ", attempting to load manually")) + var im := Image.new() + var err = im.load(path) + if err != OK: + logger.error(str("Failed to load image ", path)) + return null + var itex := ImageTexture.create_from_image(im) + return itex + diff --git a/game/addons/zylann.hterrain/tools/util/interval_slider.gd b/game/addons/zylann.hterrain/tools/util/interval_slider.gd new file mode 100644 index 0000000..481d08b --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/interval_slider.gd @@ -0,0 +1,197 @@ + +# Slider with two handles representing an interval. + +@tool +extends Control + +const VALUE_LOW = 0 +const VALUE_HIGH = 1 +const VALUE_COUNT = 2 + +const FG_MARGIN = 1 + +signal changed + +var _min_value := 0.0 +var _max_value := 1.0 +var _values = [0.2, 0.6] +var _grabbing := false + + +func _get_property_list(): + return [ + { + "name": "min_value", + "type": TYPE_FLOAT, + "usage": PROPERTY_USAGE_EDITOR + }, + { + "name": "max_value", + "type": TYPE_FLOAT, + "usage": PROPERTY_USAGE_EDITOR + }, + { + "name": "range", + "type": TYPE_VECTOR2, + "usage": PROPERTY_USAGE_STORAGE + } + ] + + +func _get(key: StringName): + match key: + &"min_value": + return _min_value + &"max_value": + return _max_value + &"range": + return Vector2(_min_value, _max_value) + + +func _set(key: StringName, value): + match key: + &"min_value": + _min_value = min(value, _max_value) + queue_redraw() + &"max_value": + _max_value = max(value, _min_value) + queue_redraw() + &"range": + _min_value = value.x + _max_value = value.y + + +func set_values(low: float, high: float): + if low > high: + low = high + if high < low: + high = low + _values[VALUE_LOW] = low + _values[VALUE_HIGH] = high + queue_redraw() + + +func set_value(i: int, v: float, notify_change: bool): + var min_value = _min_value + var max_value = _max_value + + match i: + VALUE_LOW: + max_value = _values[VALUE_HIGH] + VALUE_HIGH: + min_value = _values[VALUE_LOW] + _: + assert(false) + + v = clampf(v, min_value, max_value) + if v != _values[i]: + _values[i] = v + queue_redraw() + if notify_change: + changed.emit() + + +func get_value(i: int) -> float: + return _values[i] + + +func get_low_value() -> float: + return _values[VALUE_LOW] + + +func get_high_value() -> float: + return _values[VALUE_HIGH] + + +func get_ratio(i: int) -> float: + return _value_to_ratio(_values[i]) + + +func get_low_ratio() -> float: + return get_ratio(VALUE_LOW) + + +func get_high_ratio() -> float: + return get_ratio(VALUE_HIGH) + + +func _ratio_to_value(r: float) -> float: + return r * (_max_value - _min_value) + _min_value + + +func _value_to_ratio(v: float) -> float: + if absf(_max_value - _min_value) < 0.001: + return 0.0 + return (v - _min_value) / (_max_value - _min_value) + + +func _get_closest_index(ratio: float) -> int: + var distance_low := absf(ratio - get_low_ratio()) + var distance_high := absf(ratio - get_high_ratio()) + if distance_low < distance_high: + return VALUE_LOW + return VALUE_HIGH + + +func _set_from_pixel(px: float): + var r := (px - FG_MARGIN) / (size.x - FG_MARGIN * 2.0) + var i := _get_closest_index(r) + var v := _ratio_to_value(r) + set_value(i, v, true) + + +func _gui_input(event: InputEvent): + if event is InputEventMouseButton: + if event.pressed: + if event.button_index == MOUSE_BUTTON_LEFT: + _grabbing = true + _set_from_pixel(event.position.x) + else: + if event.button_index == MOUSE_BUTTON_LEFT: + _grabbing = false + + elif event is InputEventMouseMotion: + if _grabbing: + _set_from_pixel(event.position.x) + + +func _draw(): + var grabber_width := 3 + var background_v_margin := 0 + var foreground_margin := FG_MARGIN + var grabber_color := Color(0.8, 0.8, 0.8) + var interval_color := Color(0.4,0.4,0.4) + var background_color := Color(0.1, 0.1, 0.1) + + var control_rect := Rect2(Vector2(), size) + + var bg_rect := Rect2( + control_rect.position.x, + control_rect.position.y + background_v_margin, + control_rect.size.x, + control_rect.size.y - 2 * background_v_margin) + draw_rect(bg_rect, background_color) + + var fg_rect := control_rect.grow(-foreground_margin) + + var low_ratio := get_low_ratio() + var high_ratio := get_high_ratio() + + var low_x := fg_rect.position.x + low_ratio * fg_rect.size.x + var high_x := fg_rect.position.x + high_ratio * fg_rect.size.x + + var interval_rect := Rect2( + low_x, fg_rect.position.y, high_x - low_x, fg_rect.size.y) + draw_rect(interval_rect, interval_color) + + low_x = fg_rect.position.x + low_ratio * (fg_rect.size.x - grabber_width) + high_x = fg_rect.position.x + high_ratio * (fg_rect.size.x - grabber_width) + + for x in [low_x, high_x]: + var grabber_rect := Rect2( + x, + fg_rect.position.y, + grabber_width, + fg_rect.size.y) + draw_rect(grabber_rect, grabber_color) + diff --git a/game/addons/zylann.hterrain/tools/util/resource_importer_texture.gd b/game/addons/zylann.hterrain/tools/util/resource_importer_texture.gd new file mode 100644 index 0000000..b24f0e2 --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/resource_importer_texture.gd @@ -0,0 +1,19 @@ +@tool + +# Stuff not exposed by Godot for making .import files + +const COMPRESS_LOSSLESS = 0 +const COMPRESS_LOSSY = 1 +const COMPRESS_VRAM_COMPRESSED = 2 +const COMPRESS_VRAM_UNCOMPRESSED = 3 +const COMPRESS_BASIS_UNIVERSAL = 4 + +const ROUGHNESS_DETECT = 0 +const ROUGHNESS_DISABLED = 1 +# Godot internally subtracts 2 to magically obtain a `Image.RoughnessChannel` enum +# (also not exposed) +const ROUGHNESS_RED = 2 +const ROUGHNESS_GREEN = 3 +const ROUGHNESS_BLUE = 4 +const ROUGHNESS_ALPHA = 5 +const ROUGHNESS_GRAY = 6 diff --git a/game/addons/zylann.hterrain/tools/util/resource_importer_texture_layered.gd b/game/addons/zylann.hterrain/tools/util/resource_importer_texture_layered.gd new file mode 100644 index 0000000..ff77b1a --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/resource_importer_texture_layered.gd @@ -0,0 +1,9 @@ +@tool + +# Stuff not exposed by Godot for making .import files + +const COMPRESS_LOSSLESS = 0 +const COMPRESS_LOSSY = 1 +const COMPRESS_VRAM_COMPRESSED = 2 +const COMPRESS_VRAM_UNCOMPRESSED = 3 +const COMPRESS_BASIS_UNIVERSAL = 4 diff --git a/game/addons/zylann.hterrain/tools/util/result.gd b/game/addons/zylann.hterrain/tools/util/result.gd new file mode 100644 index 0000000..5e897f1 --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/result.gd @@ -0,0 +1,36 @@ +# Data structure to hold the result of a function that can be expected to fail. +# The use case is to report errors back to the GUI and act accordingly, +# instead of forgetting them to the console or having the script break on an assertion. +# This is a C-like way of things, where the result can bubble, and does not require globals. + +@tool + +# Replace `success` with `error : int`? +var success := false +var value = null +var message := "" +var inner_result = null + + +func _init(p_success: bool, p_message := "", p_inner = null): + success = p_success + message = p_message + inner_result = p_inner + + +# TODO Can't type-hint self return +func with_value(v): + value = v + return self + + +func get_message() -> String: + var msg := message + if inner_result != null: + msg += "\n" + msg += inner_result.get_message() + return msg + + +func is_ok() -> bool: + return success diff --git a/game/addons/zylann.hterrain/tools/util/rich_text_label_hyperlinks.gd b/game/addons/zylann.hterrain/tools/util/rich_text_label_hyperlinks.gd new file mode 100644 index 0000000..242fe6e --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/rich_text_label_hyperlinks.gd @@ -0,0 +1,11 @@ +@tool +extends RichTextLabel + + +func _init(): + meta_clicked.connect(_on_meta_clicked) + + +func _on_meta_clicked(meta): + OS.shell_open(meta) + diff --git a/game/addons/zylann.hterrain/tools/util/spin_slider.gd b/game/addons/zylann.hterrain/tools/util/spin_slider.gd new file mode 100644 index 0000000..fa690d5 --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/spin_slider.gd @@ -0,0 +1,385 @@ +@tool +extends Control + +const FG_MARGIN = 2 +const MAX_DECIMALS_VISUAL = 3 + +signal value_changed(value) + + +var _value := 0.0 +@export var value: float: + get: + return _value + set(v): + set_value_no_notify(v) + + +var _min_value := 0.0 +@export var min_value: float: + get: + return _min_value + set(v): + set_min_value(v) + + +var _max_value := 100.0 +@export var max_value: float: + get: + return _max_value + set(v): + set_max_value(v) + + +var _prefix := "" +@export var prefix: String: + get: + return _prefix + set(v): + set_prefix(v) + + +var _suffix := "" +@export var suffix: String: + get: + return _suffix + set(v): + set_suffix(v) + + +var _rounded := false +@export var rounded: bool: + get: + return _rounded + set(v): + set_rounded(v) + + +var _centered := true +@export var centered: bool: + get: + return _centered + set(v): + set_centered(v) + + +var _allow_greater := false +@export var allow_greater: bool: + get: + return _allow_greater + set(v): + set_allow_greater(v) + + +# There is still a limit when typing a larger value, but this one is to prevent software +# crashes or freezes. The regular min and max values are for slider UX. Exceeding it should be +# a corner case. +var _greater_max_value := 10000.0 +@export var greater_max_value: float: + get: + return _greater_max_value + set(v): + set_greater_max_value(v) + + +var _label : Label +var _label2 : Label +var _line_edit : LineEdit +var _ignore_line_edit := false +var _pressing := false +var _grabbing := false +var _press_pos := Vector2() + + +func _init(): + custom_minimum_size = Vector2(32, 28) + + _label = Label.new() + _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _label.clip_text = true + #_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _label.anchor_top = 0 + _label.anchor_left = 0 + _label.anchor_right = 1 + _label.anchor_bottom = 1 + _label.mouse_filter = Control.MOUSE_FILTER_IGNORE + _label.add_theme_color_override("font_color_shadow", Color(0,0,0,0.5)) + _label.add_theme_constant_override("shadow_offset_x", 1) + _label.add_theme_constant_override("shadow_offset_y", 1) + add_child(_label) + + _label2 = Label.new() + _label2.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + _label2.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _label2.clip_text = true + #_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _label2.anchor_top = 0 + _label2.anchor_left = 0 + _label2.anchor_right = 1 + _label2.anchor_bottom = 1 + _label2.offset_left = 8 + _label2.mouse_filter = Control.MOUSE_FILTER_IGNORE + _label2.add_theme_color_override("font_color_shadow", Color(0,0,0,0.5)) + _label2.add_theme_constant_override("shadow_offset_x", 1) + _label2.add_theme_constant_override("shadow_offset_y", 1) + _label2.hide() + add_child(_label2) + + _line_edit = LineEdit.new() + _line_edit.alignment = HORIZONTAL_ALIGNMENT_CENTER + _line_edit.anchor_top = 0 + _line_edit.anchor_left = 0 + _line_edit.anchor_right = 1 + _line_edit.anchor_bottom = 1 + _line_edit.gui_input.connect(_on_LineEdit_gui_input) + _line_edit.focus_exited.connect(_on_LineEdit_focus_exited) + _line_edit.text_submitted.connect(_on_LineEdit_text_submitted) + _line_edit.hide() + add_child(_line_edit) + + mouse_default_cursor_shape = Control.CURSOR_HSIZE + + +func _ready(): + pass # Replace with function body. + + +func set_centered(p_centered: bool): + _centered = p_centered + if _centered: + _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + _label.offset_right = 0 + _label2.hide() + else: + _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT + _label.offset_right = -8 + _label2.show() + queue_redraw() + + +func is_centered() -> bool: + return _centered + + +func set_value_no_notify(v: float): + set_value(v, false, false) + + +func set_value(v: float, notify_change: bool, use_slider_maximum: bool = false): + if _allow_greater and not use_slider_maximum: + v = clampf(v, _min_value, _greater_max_value) + else: + v = clampf(v, _min_value, _max_value) + + if v != _value: + _value = v + + queue_redraw() + + if notify_change: + value_changed.emit(get_value()) + + +func get_value(): + if _rounded: + return int(roundf(_value)) + return _value + + +func set_min_value(minv: float): + _min_value = minv + #queue_redraw() + + +func get_min_value() -> float: + return _min_value + + +func set_max_value(maxv: float): + _max_value = maxv + #queue_redraw() + + +func get_max_value() -> float: + return _max_value + + +func set_greater_max_value(gmax: float): + _greater_max_value = gmax + + +func get_greater_max_value() -> float: + return _greater_max_value + + +func set_rounded(b: bool): + _rounded = b + queue_redraw() + + +func is_rounded() -> bool: + return _rounded + + +func set_prefix(p_prefix: String): + _prefix = p_prefix + queue_redraw() + + +func get_prefix() -> String: + return _prefix + + +func set_suffix(p_suffix: String): + _suffix = p_suffix + queue_redraw() + + +func get_suffix() -> String: + return _suffix + + +func set_allow_greater(allow: bool): + _allow_greater = allow + + +func is_allowing_greater() -> bool: + return _allow_greater + + +func _set_from_pixel(px: float): + var r := (px - FG_MARGIN) / (size.x - FG_MARGIN * 2.0) + var v := _ratio_to_value(r) + set_value(v, true, true) + + +func get_ratio() -> float: + return _value_to_ratio(get_value()) + + +func _ratio_to_value(r: float) -> float: + return r * (_max_value - _min_value) + _min_value + + +func _value_to_ratio(v: float) -> float: + if absf(_max_value - _min_value) < 0.001: + return 0.0 + return (v - _min_value) / (_max_value - _min_value) + + +func _on_LineEdit_gui_input(event: InputEvent): + if event is InputEventKey: + if event.pressed: + if event.keycode == KEY_ESCAPE: + _ignore_line_edit = true + _hide_line_edit() + grab_focus() + _ignore_line_edit = false + + +func _on_LineEdit_focus_exited(): + if _ignore_line_edit: + return + _enter_text() + + +func _on_LineEdit_text_submitted(text: String): + _enter_text() + + +func _enter_text(): + var s = _line_edit.text.strip_edges() + if s.is_valid_float(): + var v := s.to_float() + if not _allow_greater: + v = minf(v, _max_value) + set_value(v, true, false) + _hide_line_edit() + + +func _hide_line_edit(): + _line_edit.hide() + _label.show() + queue_redraw() + + +func _show_line_edit(): + _line_edit.show() + _line_edit.text = str(get_value()) + _line_edit.select_all() + _line_edit.grab_focus() + _label.hide() + queue_redraw() + + +func _gui_input(event: InputEvent): + if event is InputEventMouseButton: + if event.pressed: + if event.button_index == MOUSE_BUTTON_LEFT: + _press_pos = event.position + _pressing = true + else: + if event.button_index == MOUSE_BUTTON_LEFT: + _pressing = false + if _grabbing: + _grabbing = false + _set_from_pixel(event.position.x) + else: + _show_line_edit() + + elif event is InputEventMouseMotion: + if _pressing and _press_pos.distance_to(event.position) > 2.0: + _grabbing = true + if _grabbing: + _set_from_pixel(event.position.x) + + +func _draw(): + if _line_edit.visible: + return + + #var grabber_width := 3 + var background_v_margin := 0 + var foreground_margin := FG_MARGIN + #var grabber_color := Color(0.8, 0.8, 0.8) + var interval_color := Color(0.4,0.4,0.4) + var background_color := Color(0.1, 0.1, 0.1) + + var control_rect := Rect2(Vector2(), size) + + var bg_rect := Rect2( + control_rect.position.x, + control_rect.position.y + background_v_margin, + control_rect.size.x, + control_rect.size.y - 2 * background_v_margin) + draw_rect(bg_rect, background_color) + + var fg_rect := control_rect.grow(-foreground_margin) + # Clamping the ratio because the value can be allowed to exceed the slider's boundaries + var ratio := clampf(get_ratio(), 0.0, 1.0) + fg_rect.size.x *= ratio + draw_rect(fg_rect, interval_color) + + var value_text := str(get_value()) + + var dot_pos := value_text.find(".") + if dot_pos != -1: + var decimal_count := len(value_text) - dot_pos + if decimal_count > MAX_DECIMALS_VISUAL: + value_text = value_text.substr(0, dot_pos + MAX_DECIMALS_VISUAL + 1) + + if _centered: + var text := value_text + if _prefix != "": + text = str(_prefix, " ", text) + if _suffix != "": + text = str(text, " ", _suffix) + _label.text = text + + else: + _label2.text = _prefix + var text := value_text + if _suffix != "": + text = str(text, " ", _suffix) + _label.text = text diff --git a/game/addons/zylann.hterrain/tools/util/spin_slider.tscn b/game/addons/zylann.hterrain/tools/util/spin_slider.tscn new file mode 100644 index 0000000..70a0da8 --- /dev/null +++ b/game/addons/zylann.hterrain/tools/util/spin_slider.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/zylann.hterrain/tools/util/spin_slider.gd" type="Script" id=1] + +[node name="SpinSlider" type="Control"] +anchor_right = 1.0 +anchor_bottom = 1.0 +rect_min_size = Vector2( 32, 28 ) +mouse_default_cursor_shape = 10 +script = ExtResource( 1 ) +__meta__ = { +"_edit_use_anchors_": false +} |