310 lines
11 KiB
GDScript
310 lines
11 KiB
GDScript
# domain_range.gd
|
|
# This file is part of: SimpleGrassTextured
|
|
# Copyright (c) 2025 IcterusGames
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining
|
|
# a copy of this software and associated documentation files (the
|
|
# "Software"), to deal in the Software without restriction, including
|
|
# without limitation the rights to use, copy, modify, merge, publish,
|
|
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
# permit persons to whom the Software is furnished to do so, subject to
|
|
# the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
@tool
|
|
extends Control
|
|
|
|
signal value_changed(value_min: float, value_max: float)
|
|
|
|
@export var range_min: float = 0.0
|
|
@export var range_max: float = 180.0
|
|
@export var step: float = 1.0
|
|
@export var value_min: float = 0.0 : set = set_value_min
|
|
@export var value_max: float = 45.0 : set = set_value_max
|
|
|
|
var _ed_scale: float = 1.0
|
|
var _slider_area := Rect2()
|
|
var _slider_range := Rect2()
|
|
var _grabber_min_pos := Vector2.ZERO
|
|
var _grabber_max_pos := Vector2.ZERO
|
|
var _is_grabbing_min_grab := false
|
|
var _is_grabbing_max_grab := false
|
|
var _is_grabbing_min_value := false
|
|
var _is_grabbing_max_value := false
|
|
var _is_editing_min := false
|
|
var _is_editing_max := false
|
|
var _mouse_over := false
|
|
var _mouse_click_pos := Vector2.ONE * 100000
|
|
var _mouse_click_rel := Vector2.ZERO
|
|
var _mouse_click_value := 0.0
|
|
var _mouse_over_grabber_min := false
|
|
var _mouse_over_grabber_max := false
|
|
|
|
@onready var _line_edit: LineEdit = $LineEdit
|
|
|
|
|
|
func _ready() -> void:
|
|
theme_changed.connect(_on_theme_changed)
|
|
_on_theme_changed()
|
|
_update_tooltip()
|
|
|
|
|
|
func _notification(what: int) -> void:
|
|
if what == NOTIFICATION_MOUSE_ENTER_SELF:
|
|
_mouse_over = true
|
|
queue_redraw()
|
|
if what == NOTIFICATION_MOUSE_EXIT_SELF:
|
|
_mouse_over = false
|
|
_mouse_over_grabber_min = false
|
|
_mouse_over_grabber_max = false
|
|
queue_redraw()
|
|
|
|
|
|
func _gui_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseMotion:
|
|
_mouse_over_grabber_min = false
|
|
_mouse_over_grabber_max = false
|
|
if _is_grabbing_max_value:
|
|
if event.position.distance_to(_mouse_click_pos) > 8 * _ed_scale:
|
|
_mouse_click_pos = Vector2.ONE * 100000
|
|
set_value_max(value_max + event.screen_relative.x * step)
|
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
elif _is_grabbing_min_value:
|
|
if event.position.distance_to(_mouse_click_pos) > 8 * _ed_scale:
|
|
_mouse_click_pos = Vector2.ONE * 100000
|
|
set_value_min(value_min + event.screen_relative.x * step)
|
|
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
|
|
elif _is_grabbing_max_grab:
|
|
_mouse_click_pos = Vector2.ONE * 100000
|
|
set_value_max((event.position.x - _mouse_click_rel.x - _slider_area.position.x) / _slider_area.size.x * range_max)
|
|
elif _is_grabbing_min_grab:
|
|
_mouse_click_pos = Vector2.ONE * 100000
|
|
set_value_min((event.position.x - _mouse_click_rel.x - _slider_area.position.x) / _slider_area.size.x * range_max)
|
|
if event.position.distance_to(_grabber_max_pos) <= 8 * _ed_scale:
|
|
_mouse_over_grabber_max = true
|
|
if event.position.distance_to(_grabber_min_pos) <= 8 * _ed_scale:
|
|
_mouse_over_grabber_min = true
|
|
queue_redraw()
|
|
|
|
if event is InputEventMouseButton:
|
|
if event.button_index == MOUSE_BUTTON_LEFT:
|
|
if event.pressed:
|
|
_mouse_click_pos = event.position
|
|
if (event.position.distance_to(_grabber_min_pos) <= 8 * _ed_scale and
|
|
event.position.distance_to(_grabber_max_pos) <= 8 * _ed_scale):
|
|
if event.position.x < (_grabber_min_pos.x + _grabber_max_pos.x) / 2:
|
|
_is_grabbing_min_grab = true
|
|
_mouse_click_rel = event.position - _grabber_min_pos
|
|
_mouse_click_value = value_min
|
|
else:
|
|
_is_grabbing_max_grab = true
|
|
_mouse_click_rel = event.position - _grabber_max_pos
|
|
_mouse_click_value = value_max
|
|
elif event.position.distance_to(_grabber_min_pos) <= 8 * _ed_scale:
|
|
_is_grabbing_min_grab = true
|
|
_mouse_click_rel = event.position - _grabber_min_pos
|
|
_mouse_click_value = value_min
|
|
elif event.position.distance_to(_grabber_max_pos) <= 8 * _ed_scale:
|
|
_is_grabbing_max_grab = true
|
|
_mouse_click_rel = event.position - _grabber_max_pos
|
|
_mouse_click_value = value_max
|
|
elif event.position.x < size.x / 2:
|
|
_is_grabbing_min_value = true
|
|
_mouse_click_value = value_min
|
|
else:
|
|
_is_grabbing_max_value = true
|
|
_mouse_click_value = value_max
|
|
else: # Mouse left released
|
|
if _mouse_click_pos.distance_to(event.position) <= 8 * _ed_scale:
|
|
_grab_end()
|
|
if event.position.x < size.x / 2:
|
|
_line_edit.text = str(value_min)
|
|
_is_editing_min = true
|
|
_is_editing_max = false
|
|
else:
|
|
_line_edit.text = str(value_max)
|
|
_is_editing_min = false
|
|
_is_editing_max = true
|
|
_line_edit.visible = true
|
|
_line_edit.select()
|
|
_line_edit.grab_focus()
|
|
elif _is_grabbing_max_grab or _is_grabbing_max_value:
|
|
_grab_end()
|
|
queue_redraw()
|
|
elif _is_grabbing_min_grab or _is_grabbing_min_value:
|
|
_grab_end()
|
|
queue_redraw()
|
|
elif event.button_index == MOUSE_BUTTON_RIGHT:
|
|
if event.pressed:
|
|
if _is_grabbing_max_grab or _is_grabbing_max_value:
|
|
_grab_end()
|
|
set_value_max(_mouse_click_value)
|
|
elif _is_grabbing_min_grab or _is_grabbing_min_value:
|
|
_grab_end()
|
|
set_value_min(_mouse_click_value)
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_UP or event.button_index == MOUSE_BUTTON_WHEEL_LEFT:
|
|
if event.pressed:
|
|
if event.position.x < size.x / 2:
|
|
set_value_min(value_min + step)
|
|
else:
|
|
set_value_max(value_max + step)
|
|
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN or event.button_index == MOUSE_BUTTON_WHEEL_RIGHT:
|
|
if event.pressed:
|
|
if event.position.x < size.x / 2:
|
|
set_value_min(value_min - step)
|
|
else:
|
|
set_value_max(value_max - step)
|
|
|
|
|
|
func _draw() -> void:
|
|
var sb := get_theme_stylebox(&"normal", &"LineEdit")
|
|
var font := get_theme_font(&"font", &"LineEdit")
|
|
var font_size := get_theme_font_size(&"font_size", &"LineEdit")
|
|
var label_min := "%0.0f" % value_min
|
|
var label_max := "%0.0f" % value_max
|
|
var label_w_min := font.get_string_size(label_min, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
|
|
var label_w_max := font.get_string_size(label_max, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
|
|
var label_y := (size.y - font.get_height(font_size)) / 2.0 + font.get_ascent(font_size)
|
|
var color := get_theme_color(&"font_color", &"LineEdit")
|
|
|
|
draw_style_box(sb, Rect2(Vector2.ZERO, size))
|
|
if has_focus():
|
|
var sb_focus = get_theme_stylebox(&"focus", &"LineEdit")
|
|
draw_style_box(sb_focus, Rect2(Vector2(), size))
|
|
|
|
color.a = 0.9
|
|
draw_line(Vector2(size.x / 2.0, label_y - 10.0 * _ed_scale), Vector2(size.x / 2.0, label_y), color, max(1.0, _ed_scale))
|
|
|
|
draw_string(font, Vector2(size.x * 0.25 - label_w_min / 2.0, label_y), label_min, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color)
|
|
draw_string(font, Vector2(size.x * 0.75 - label_w_max / 2.0, label_y), label_max, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color)
|
|
|
|
_slider_area = Rect2(_ed_scale * 6.0, size.y - _ed_scale * 5.0, size.x - _ed_scale * 12.0, _ed_scale * 2.0)
|
|
color.a = 0.2
|
|
draw_rect(_slider_area, color)
|
|
|
|
_slider_range = _slider_area
|
|
_slider_range.size.x *= (value_max - value_min) / (range_max - range_min)
|
|
_slider_range.position.x += value_min / (range_max - range_min) * _slider_area.size.x
|
|
color.a = 0.45
|
|
draw_rect(_slider_range, color)
|
|
|
|
_grabber_min_pos = _slider_range.position
|
|
_grabber_max_pos = _slider_range.position + Vector2(_slider_range.size.x, 0)
|
|
|
|
color.a = 0.9
|
|
var tex: Texture2D = null
|
|
if not _is_grabbing_max_grab:
|
|
if _mouse_over_grabber_min or _is_grabbing_min_grab:
|
|
tex = get_theme_icon(&"grabber_highlight", &"HSlider")
|
|
elif _mouse_over and not _mouse_over_grabber_max:
|
|
tex = get_theme_icon(&"grabber", &"HSlider")
|
|
if tex:
|
|
draw_texture(tex, (_grabber_min_pos - tex.get_size() / 2.0) + Vector2.DOWN)
|
|
else:
|
|
draw_rect(Rect2(_grabber_min_pos.x - 2 * _ed_scale, _grabber_min_pos.y - 1 * _ed_scale, 4 * _ed_scale, 4 * _ed_scale), color)
|
|
|
|
tex = null
|
|
if not _is_grabbing_min_grab:
|
|
if _mouse_over_grabber_max or _is_grabbing_max_grab:
|
|
tex = get_theme_icon(&"grabber_highlight", &"HSlider")
|
|
elif _mouse_over and not _mouse_over_grabber_min:
|
|
tex = get_theme_icon(&"grabber", &"HSlider")
|
|
if tex:
|
|
draw_texture(tex, (_grabber_max_pos - tex.get_size() / 2.0) + Vector2.DOWN)
|
|
else:
|
|
draw_rect(Rect2(_grabber_max_pos.x - 2 * _ed_scale, _grabber_max_pos.y - 1 * _ed_scale, 4 * _ed_scale, 4 * _ed_scale), color)
|
|
|
|
|
|
func set_value(v_min: float, v_max: float) -> void:
|
|
v_min = max(min(v_min, v_max), range_min)
|
|
v_max = min(max(v_min, v_max), range_max)
|
|
if v_min != value_min or v_max != value_max:
|
|
value_min = v_min
|
|
value_max = v_max
|
|
value_changed.emit(value_min, value_max)
|
|
_update_tooltip()
|
|
queue_redraw()
|
|
|
|
|
|
func set_value_min(value: float) -> void:
|
|
value = clampf(snappedf(value, step), range_min, value_max)
|
|
if value != value_min:
|
|
value_min = value
|
|
value_changed.emit(value_min, value_max)
|
|
_update_tooltip()
|
|
queue_redraw()
|
|
|
|
|
|
func set_value_max(value: float) -> void:
|
|
value = clampf(snappedf(value, step), value_min, range_max)
|
|
if value != value_max:
|
|
value_max = value
|
|
value_changed.emit(value_min, value_max)
|
|
_update_tooltip()
|
|
queue_redraw()
|
|
|
|
|
|
func _update_tooltip() -> void:
|
|
tooltip_text = "Slope to avoid:\n"
|
|
tooltip_text += "Min: %0.*f°\n" %[step_decimals(step), value_min]
|
|
tooltip_text += "Max: %0.*f°\n" %[step_decimals(step), value_max]
|
|
|
|
|
|
func _grab_end() -> void:
|
|
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
|
|
if _is_grabbing_max_value:
|
|
Input.warp_mouse(_grabber_max_pos + global_position)
|
|
elif _is_grabbing_min_value:
|
|
Input.warp_mouse(_grabber_min_pos + global_position)
|
|
_is_grabbing_min_grab = false
|
|
_is_grabbing_max_grab = false
|
|
_is_grabbing_min_value = false
|
|
_is_grabbing_max_value = false
|
|
|
|
|
|
func _on_theme_changed() -> void:
|
|
var es: int = EditorInterface.get_editor_settings().get_setting("interface/editor/display_scale")
|
|
if es == 7:
|
|
_ed_scale = EditorInterface.get_editor_settings().get_setting("interface/editor/custom_display_scale")
|
|
else:
|
|
_ed_scale = [1.0, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0][clamp(es, 0, 6)]
|
|
_line_edit.caret_blink = EditorInterface.get_editor_settings().get_setting("text_editor/appearance/caret/caret_blink")
|
|
|
|
|
|
func _on_line_edit_text_submitted(new_text: String) -> void:
|
|
var value := range_min
|
|
var expression = Expression.new()
|
|
if expression.parse(new_text) == OK:
|
|
var result = expression.execute()
|
|
if not expression.has_execute_failed() and (typeof(result) == TYPE_FLOAT or typeof(result) == TYPE_INT):
|
|
value = result
|
|
if _is_editing_min:
|
|
set_value_min(value)
|
|
elif _is_editing_max:
|
|
set_value_max(value)
|
|
_is_editing_min = false
|
|
_is_editing_max = false
|
|
_line_edit.visible = false
|
|
grab_focus()
|
|
|
|
|
|
func _on_line_edit_focus_exited() -> void:
|
|
_line_edit.visible = false
|
|
|
|
|
|
func _on_line_edit_gui_input(event: InputEvent) -> void:
|
|
if event is InputEventKey:
|
|
if event.keycode == KEY_ESCAPE and event.pressed:
|
|
_line_edit.visible = false
|
|
grab_focus()
|