add kanban
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
## The visual representation of a kanban board.
|
||||
|
||||
|
||||
const __Singletons := preload("../../plugin_singleton/singletons.gd")
|
||||
const __Shortcuts := preload("../shortcuts.gd")
|
||||
const __EditContext := preload("../edit_context.gd")
|
||||
const __BoardData := preload("../../data/board.gd")
|
||||
const __StageScript := preload("../stage/stage.gd")
|
||||
const __StageScene := preload("../stage/stage.tscn")
|
||||
const __Filter := preload("../filter.gd")
|
||||
const __SettingsScript := preload("../settings/settings.gd")
|
||||
|
||||
signal show_documentation()
|
||||
|
||||
var board_data: __BoardData
|
||||
|
||||
@onready var search_bar: LineEdit = %SearchBar
|
||||
@onready var button_advanced_search: Button = %AdvancedSearch
|
||||
@onready var button_show_categories: Button = %ShowCategories
|
||||
@onready var button_show_descriptions: Button = %ShowDescriptions
|
||||
@onready var button_show_steps: Button = %ShowSteps
|
||||
@onready var button_documentation: Button = %Documentation
|
||||
@onready var button_settings: Button = %Settings
|
||||
@onready var column_holder: HBoxContainer = %ColumnHolder
|
||||
@onready var settings: __SettingsScript = %SettingsView
|
||||
|
||||
|
||||
func _ready():
|
||||
update()
|
||||
board_data.layout.changed.connect(update)
|
||||
|
||||
settings.board_data = board_data
|
||||
|
||||
search_bar.text_changed.connect(__on_filter_changed)
|
||||
search_bar.text_submitted.connect(__on_search_bar_entered)
|
||||
button_advanced_search.toggled.connect(__on_filter_changed)
|
||||
|
||||
button_show_categories.toggled.connect(__on_show_categories_toggled)
|
||||
button_show_descriptions.toggled.connect(__on_show_descriptions_toggled)
|
||||
button_show_steps.toggled.connect(__on_show_steps_toggled)
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
|
||||
ctx.settings.changed.connect(update)
|
||||
|
||||
ctx.filter_changed.connect(__on_filter_changed_external)
|
||||
|
||||
button_documentation.pressed.connect(func(): show_documentation.emit())
|
||||
button_documentation.visible = Engine.is_editor_hint()
|
||||
|
||||
button_settings.pressed.connect(settings.popup_centered_ratio_no_fullscreen)
|
||||
|
||||
|
||||
func _shortcut_input(event: InputEvent) -> void:
|
||||
if not __Shortcuts.should_handle_shortcut(self):
|
||||
return
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
if not event.is_echo() and event.is_pressed():
|
||||
if shortcuts.search.matches_event(event):
|
||||
search_bar.grab_focus()
|
||||
get_viewport().set_input_as_handled()
|
||||
elif shortcuts.undo.matches_event(event):
|
||||
ctx.undo_redo.undo()
|
||||
get_viewport().set_input_as_handled()
|
||||
elif shortcuts.redo.matches_event(event):
|
||||
ctx.undo_redo.redo()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
if is_instance_valid(search_bar):
|
||||
search_bar.right_icon = get_theme_icon(&"Search", &"EditorIcons")
|
||||
if is_instance_valid(button_settings):
|
||||
button_settings.icon = get_theme_icon(&"Tools", &"EditorIcons")
|
||||
if is_instance_valid(button_documentation):
|
||||
button_documentation.icon = get_theme_icon(&"Help", &"EditorIcons")
|
||||
if is_instance_valid(button_advanced_search):
|
||||
button_advanced_search.icon = get_theme_icon(&"Zoom", &"EditorIcons")
|
||||
if is_instance_valid(button_show_categories):
|
||||
button_show_categories.icon = get_theme_icon(&"Rectangle", &"EditorIcons")
|
||||
if is_instance_valid(button_show_descriptions):
|
||||
button_show_descriptions.icon = get_theme_icon(&"Script", &"EditorIcons")
|
||||
if is_instance_valid(button_show_steps):
|
||||
button_show_steps.icon = get_theme_icon(&"FileList", &"EditorIcons")
|
||||
if is_instance_valid(settings):
|
||||
settings.on_theme_changed()
|
||||
|
||||
|
||||
func update() -> void:
|
||||
for column in column_holder.get_children():
|
||||
column.queue_free()
|
||||
|
||||
for column_data in board_data.layout.columns:
|
||||
var column_scroll = ScrollContainer.new()
|
||||
column_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||
column_scroll.set_v_size_flags(Control.SIZE_EXPAND_FILL)
|
||||
column_scroll.set_h_size_flags(Control.SIZE_EXPAND_FILL)
|
||||
var column = VBoxContainer.new()
|
||||
column.set_v_size_flags(Control.SIZE_EXPAND_FILL)
|
||||
column.set_h_size_flags(Control.SIZE_EXPAND_FILL)
|
||||
|
||||
column_scroll.add_child(column)
|
||||
column_holder.add_child(column_scroll)
|
||||
|
||||
for uuid in column_data:
|
||||
var stage := __StageScene.instantiate()
|
||||
stage.board_data = board_data
|
||||
stage.data_uuid = uuid
|
||||
column.add_child(stage)
|
||||
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
button_show_categories.set_pressed_no_signal(ctx.settings.show_category_on_board)
|
||||
button_show_descriptions.set_pressed_no_signal(ctx.settings.show_description_preview)
|
||||
button_show_steps.set_pressed_no_signal(ctx.settings.show_steps_preview)
|
||||
|
||||
|
||||
# Do not use parameters the method is bound to diffrent signals.
|
||||
func __on_filter_changed(param1: Variant = null):
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
|
||||
if ctx.filter_changed.is_connected(__on_filter_changed_external):
|
||||
ctx.filter_changed.disconnect(__on_filter_changed_external)
|
||||
|
||||
ctx.filter = __Filter.new(search_bar.text, button_advanced_search.button_pressed)
|
||||
|
||||
ctx.filter_changed.connect(__on_filter_changed_external)
|
||||
|
||||
|
||||
func __on_search_bar_entered(filter: String):
|
||||
button_advanced_search.grab_focus()
|
||||
|
||||
|
||||
func __on_filter_changed_external():
|
||||
search_bar.text = ""
|
||||
|
||||
|
||||
func __on_show_categories_toggled(button_pressed: bool):
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.show_category_on_board = button_pressed
|
||||
|
||||
|
||||
func __on_show_descriptions_toggled(button_pressed: bool):
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.show_description_preview = button_pressed
|
||||
|
||||
|
||||
func __on_show_steps_toggled(button_pressed: bool):
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.show_steps_preview = button_pressed
|
||||
@@ -0,0 +1 @@
|
||||
uid://5hks8mhjyp62
|
||||
@@ -0,0 +1,89 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://c5dk4lnyiag3w"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://5hks8mhjyp62" path="res://addons/kanban_tasks/view/board/board.gd" id="1_p7lf4"]
|
||||
[ext_resource type="PackedScene" uid="uid://dh1yunmhipirg" path="res://addons/kanban_tasks/view/settings/settings.tscn" id="2_by8mq"]
|
||||
|
||||
[node name="BoardView" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 0
|
||||
theme_override_constants/separation = 5
|
||||
script = ExtResource("1_p7lf4")
|
||||
|
||||
[node name="Header" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="SearchBar" type="LineEdit" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Search"
|
||||
clear_button_enabled = true
|
||||
|
||||
[node name="AdvancedSearch" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Search in details."
|
||||
toggle_mode = true
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="Header"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "Show categories"
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="ShowCategories" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
|
||||
[node name="ShowDescriptions" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Show descriptions."
|
||||
toggle_mode = true
|
||||
|
||||
[node name="ShowSteps" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Show steps."
|
||||
toggle_mode = true
|
||||
|
||||
[node name="VSeparator2" type="VSeparator" parent="Header"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Documentation" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Open documentation."
|
||||
flat = true
|
||||
|
||||
[node name="Settings" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Manage board settings."
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 0
|
||||
vertical_scroll_mode = 0
|
||||
|
||||
[node name="ColumnHolder" type="HBoxContainer" parent="ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 5
|
||||
alignment = 1
|
||||
|
||||
[node name="SettingsView" parent="." instance=ExtResource("2_by8mq")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
@@ -0,0 +1,44 @@
|
||||
@tool
|
||||
extends PopupMenu
|
||||
|
||||
|
||||
const __BoardData := preload("../../data/board.gd")
|
||||
|
||||
var board_data: __BoardData
|
||||
|
||||
signal uuid_selected(uuid)
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
about_to_popup.connect(__update_items_from_board)
|
||||
id_pressed.connect(__on_id_pressed)
|
||||
|
||||
|
||||
func popup_at_local_position(source: CanvasItem, local_position: Vector2) -> void:
|
||||
popup_at_global_position(source, source.get_global_transform() * local_position)
|
||||
|
||||
|
||||
func popup_at_global_position(source: CanvasItem, global_position: Vector2) -> void:
|
||||
position = global_position
|
||||
if not source.get_window().gui_embed_subwindows:
|
||||
position += source.get_window().position
|
||||
popup()
|
||||
|
||||
|
||||
func popup_at_mouse_position(source: CanvasItem) -> void:
|
||||
popup_at_global_position(source, source.get_global_mouse_position())
|
||||
|
||||
|
||||
func __update_items_from_board() -> void:
|
||||
clear()
|
||||
size = Vector2i.ZERO
|
||||
for uuid in board_data.get_categories():
|
||||
var i = Image.create(16, 16, false, Image.FORMAT_RGB8)
|
||||
i.fill(board_data.get_category(uuid).color)
|
||||
var t = ImageTexture.create_from_image(i)
|
||||
add_icon_item(t, board_data.get_category(uuid).title)
|
||||
set_item_metadata(-1, uuid)
|
||||
|
||||
|
||||
func __on_id_pressed(id) -> void:
|
||||
uuid_selected.emit(get_item_metadata(id))
|
||||
@@ -0,0 +1 @@
|
||||
uid://pv2ghsb6w6fn
|
||||
@@ -0,0 +1,225 @@
|
||||
@tool
|
||||
extends AcceptDialog
|
||||
|
||||
|
||||
const __BoardData := preload("../../data/board.gd")
|
||||
const __StepData := preload("../../data/step.gd")
|
||||
const __StepEntry := preload("../details/step_entry.gd")
|
||||
const __Singletons := preload("../../plugin_singleton/singletons.gd")
|
||||
const __EditContext := preload("../edit_context.gd")
|
||||
|
||||
var board_data: __BoardData
|
||||
var data_uuid: String
|
||||
|
||||
var __step_data: __StepData
|
||||
|
||||
@onready var category_select: OptionButton = %Category
|
||||
@onready var h_split_container: HSplitContainer = %HSplitContainer
|
||||
@onready var description_edit: TextEdit = %Description
|
||||
@onready var step_holder: VBoxContainer = %StepHolder
|
||||
@onready var steps_panel_container: PanelContainer = %PanelContainer
|
||||
@onready var create_step_edit: LineEdit = %CreateStepEdit
|
||||
@onready var step_details: VBoxContainer = %StepDetails
|
||||
@onready var close_step_details_button: Button = %CloseStepDetails
|
||||
@onready var step_edit: TextEdit = %StepEdit
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
about_to_popup.connect(__on_about_to_popup)
|
||||
create_step_edit.text_submitted.connect(__create_step)
|
||||
close_step_details_button.pressed.connect(__close_step_details)
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
step_holder.entry_action_triggered.connect(__on_step_action_triggered)
|
||||
step_holder.entry_move_requesed.connect(__step_move_requesed)
|
||||
|
||||
visibility_changed.connect(__save_internal_state)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
if is_instance_valid(steps_panel_container):
|
||||
steps_panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Panel"))
|
||||
if is_instance_valid(create_step_edit):
|
||||
create_step_edit.right_icon = get_theme_icon(&"Add", &"EditorIcons")
|
||||
if is_instance_valid(close_step_details_button):
|
||||
close_step_details_button.icon = get_theme_icon(&"Close", &"EditorIcons")
|
||||
|
||||
|
||||
func update() -> void:
|
||||
if description_edit.text_changed.is_connected(__on_description_changed):
|
||||
description_edit.text_changed.disconnect(__on_description_changed)
|
||||
if description_edit.text != board_data.get_task(data_uuid).description:
|
||||
description_edit.text = board_data.get_task(data_uuid).description
|
||||
description_edit.text_changed.connect(__on_description_changed)
|
||||
|
||||
title = "Task Details: " + board_data.get_task(data_uuid).title
|
||||
|
||||
if category_select.item_selected.is_connected(__on_category_selected):
|
||||
category_select.item_selected.disconnect(__on_category_selected)
|
||||
category_select.clear()
|
||||
for uuid in board_data.get_categories():
|
||||
var i = Image.create(16, 16, false, Image.FORMAT_RGB8)
|
||||
i.fill(board_data.get_category(uuid).color)
|
||||
var t = ImageTexture.create_from_image(i)
|
||||
category_select.add_icon_item(t, board_data.get_category(uuid).title)
|
||||
category_select.set_item_metadata(-1, uuid)
|
||||
if uuid == board_data.get_task(data_uuid).category:
|
||||
category_select.select(category_select.item_count - 1)
|
||||
|
||||
category_select.item_selected.connect(__on_category_selected)
|
||||
|
||||
step_holder.clear_steps()
|
||||
for step in board_data.get_task(data_uuid).steps:
|
||||
step_holder.add_step(step)
|
||||
for entry in step_holder.get_step_entries():
|
||||
entry.being_edited = (entry.step_data == __step_data)
|
||||
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
|
||||
step_details.visible = is_instance_valid(__step_data)
|
||||
description_edit.visible = not (ctx.settings.edit_step_details_exclusively and is_instance_valid(__step_data))
|
||||
if is_instance_valid(__step_data):
|
||||
if step_edit.text_changed.is_connected(__on_step_details_changed):
|
||||
step_edit.text_changed.disconnect(__on_step_details_changed)
|
||||
step_edit.text = __step_data.details
|
||||
step_edit.text_changed.connect(__on_step_details_changed)
|
||||
|
||||
|
||||
# Workaround for godotengine/godot#70451
|
||||
func popup_centered_ratio_no_fullscreen(ratio: float = 0.8) -> void:
|
||||
var viewport: Viewport = get_parent().get_viewport()
|
||||
popup(Rect2i(Vector2(viewport.position) + viewport.size / 2.0 - viewport.size * ratio / 2.0, viewport.size * ratio))
|
||||
|
||||
|
||||
func edit_step_details(step: __StepData) -> void:
|
||||
if is_instance_valid(__step_data):
|
||||
__step_data.changed.disconnect(update)
|
||||
__step_data = step
|
||||
__step_data.changed.connect(update)
|
||||
update()
|
||||
step_edit.set_caret_line(step_edit.get_line_count())
|
||||
step_edit.set_caret_column(len(step_edit.get_line(step_edit.get_line_count() - 1)))
|
||||
step_edit.grab_focus.call_deferred()
|
||||
|
||||
|
||||
func move_step_up(step: __StepData) -> void:
|
||||
var steps = board_data.get_task(data_uuid).steps
|
||||
if step in steps and steps[0] != step:
|
||||
var index = steps.find(step)
|
||||
steps.erase(step)
|
||||
steps.insert(index - 1, step)
|
||||
board_data.get_task(data_uuid).steps = steps
|
||||
update()
|
||||
|
||||
|
||||
func move_step_down(step: __StepData) -> void:
|
||||
var steps = board_data.get_task(data_uuid).steps
|
||||
if step in steps and steps[-1] != step:
|
||||
var index = steps.find(step)
|
||||
steps.erase(step)
|
||||
steps.insert(index + 1, step)
|
||||
board_data.get_task(data_uuid).steps = steps
|
||||
update()
|
||||
|
||||
|
||||
func delete_step(step: __StepData) -> void:
|
||||
close_step_details(step)
|
||||
var steps = board_data.get_task(data_uuid).steps
|
||||
if step in steps:
|
||||
steps.erase(step)
|
||||
board_data.get_task(data_uuid).steps = steps
|
||||
update()
|
||||
|
||||
|
||||
func close_step_details(step: __StepData) -> void:
|
||||
if __step_data == step:
|
||||
__close_step_details()
|
||||
|
||||
|
||||
func __on_step_action_triggered(entry: __StepEntry, action: __StepEntry.Actions) -> void:
|
||||
match action:
|
||||
__StepEntry.Actions.EDIT_HARD:
|
||||
edit_step_details(entry.step_data)
|
||||
__StepEntry.Actions.EDIT_SOFT:
|
||||
if is_instance_valid(__step_data):
|
||||
edit_step_details(entry.step_data)
|
||||
__StepEntry.Actions.CLOSE:
|
||||
close_step_details(entry.step_data)
|
||||
__StepEntry.Actions.DELETE:
|
||||
delete_step(entry.step_data)
|
||||
__StepEntry.Actions.MOVE_UP:
|
||||
move_step_up(entry.step_data)
|
||||
__StepEntry.Actions.MOVE_DOWN:
|
||||
move_step_down(entry.step_data)
|
||||
|
||||
|
||||
func __step_move_requesed(moved_entry: __StepEntry, target_entry: __StepEntry, move_after_target: bool) -> void:
|
||||
var steps = board_data.get_task(data_uuid).steps
|
||||
var moved_idx = steps.find(moved_entry.step_data)
|
||||
var target_idx = steps.find(target_entry.step_data)
|
||||
if moved_idx < 0 or target_idx < 0 or moved_idx == target_idx:
|
||||
return
|
||||
steps.erase(moved_entry.step_data)
|
||||
if moved_idx < target_idx:
|
||||
target_idx -= 1
|
||||
if move_after_target:
|
||||
steps.insert(target_idx + 1, moved_entry.step_data)
|
||||
else:
|
||||
steps.insert(target_idx, moved_entry.step_data)
|
||||
board_data.get_task(data_uuid).steps = steps
|
||||
update()
|
||||
|
||||
|
||||
func __load_internal_state() -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
if ctx.settings.internal_states.has("details_editor_step_holder_width"):
|
||||
h_split_container.split_offset = ctx.settings.internal_states["details_editor_step_holder_width"]
|
||||
|
||||
|
||||
func __save_internal_state() -> void:
|
||||
if not visible:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.set_internal_state("details_editor_step_holder_width", h_split_container.split_offset)
|
||||
|
||||
|
||||
func __close_step_details() -> void:
|
||||
__step_data.changed.disconnect(update)
|
||||
__step_data = null
|
||||
update()
|
||||
|
||||
|
||||
func __on_step_details_changed() -> void:
|
||||
if __step_data.changed.is_connected(update):
|
||||
__step_data.changed.disconnect(update)
|
||||
__step_data.details = step_edit.text
|
||||
__step_data.changed.connect(update)
|
||||
|
||||
|
||||
func __on_about_to_popup() -> void:
|
||||
if is_instance_valid(__step_data):
|
||||
__close_step_details()
|
||||
update()
|
||||
__load_internal_state()
|
||||
if board_data.get_task(data_uuid).description.is_empty():
|
||||
description_edit.grab_focus.call_deferred()
|
||||
|
||||
|
||||
func __on_description_changed() -> void:
|
||||
board_data.get_task(data_uuid).description = description_edit.text
|
||||
|
||||
|
||||
func __on_category_selected(index: int) -> void:
|
||||
board_data.get_task(data_uuid).category = category_select.get_item_metadata(index)
|
||||
|
||||
|
||||
func __create_step(text: String) -> void:
|
||||
if text.is_empty():
|
||||
return
|
||||
var task = board_data.get_task(data_uuid)
|
||||
var data = __StepData.new(text)
|
||||
task.add_step(data)
|
||||
create_step_edit.text = ""
|
||||
update()
|
||||
for step in step_holder.get_step_entries():
|
||||
if step.step_data == data:
|
||||
step.grab_focus.call_deferred()
|
||||
@@ -0,0 +1 @@
|
||||
uid://cfuka8bfmcwog
|
||||
@@ -0,0 +1,100 @@
|
||||
[gd_scene format=3 uid="uid://bwi22eyrmeeet"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cfuka8bfmcwog" path="res://addons/kanban_tasks/view/details/details.gd" id="1_gh7s6"]
|
||||
[ext_resource type="PackedScene" uid="uid://dwjg5vyxx4g48" path="res://addons/kanban_tasks/view/details/step_holder.tscn" id="2_0ptaf"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g2k57"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 5.0
|
||||
bg_color = Color(0.1, 0.1, 0.1, 0.6)
|
||||
corner_radius_top_left = 3
|
||||
corner_radius_top_right = 3
|
||||
corner_radius_bottom_right = 3
|
||||
corner_radius_bottom_left = 3
|
||||
corner_detail = 5
|
||||
|
||||
[node name="Details" type="AcceptDialog" unique_id=537054767]
|
||||
oversampling_override = 1.0
|
||||
title = "Task Details"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(916, 557)
|
||||
ok_button_text = "Close"
|
||||
script = ExtResource("1_gh7s6")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1730906135]
|
||||
custom_minimum_size = Vector2(900, 500)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Category" type="OptionButton" parent="VBoxContainer" unique_id=879562296]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSplitContainer" type="HSplitContainer" parent="VBoxContainer" unique_id=1738893748]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VSplitContainer" type="VSplitContainer" parent="VBoxContainer/HSplitContainer" unique_id=1951989716]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="Description" type="TextEdit" parent="VBoxContainer/HSplitContainer/VSplitContainer" unique_id=2088816932]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
wrap_mode = 1
|
||||
|
||||
[node name="StepDetails" type="VBoxContainer" parent="VBoxContainer/HSplitContainer/VSplitContainer" unique_id=1125161346]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails" unique_id=2007120957]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails/HBoxContainer" unique_id=609616893]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Step Details:"
|
||||
|
||||
[node name="CloseStepDetails" type="Button" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails/HBoxContainer" unique_id=1389055250]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Close"
|
||||
flat = true
|
||||
|
||||
[node name="StepEdit" type="TextEdit" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails" unique_id=2005028969]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
wrap_mode = 1
|
||||
|
||||
[node name="StepList" type="VBoxContainer" parent="VBoxContainer/HSplitContainer" unique_id=1447925866]
|
||||
custom_minimum_size = Vector2(200, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CreateStepEdit" type="LineEdit" parent="VBoxContainer/HSplitContainer/StepList" unique_id=479211446]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
placeholder_text = "Create Step"
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/HSplitContainer/StepList" unique_id=1566602760]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_g2k57")
|
||||
|
||||
[node name="StepHolder" parent="VBoxContainer/HSplitContainer/StepList/PanelContainer" unique_id=128405745 instance=ExtResource("2_0ptaf")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
steps_focus_mode = 2
|
||||
@@ -0,0 +1,135 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
## Visual representation of a step.
|
||||
|
||||
|
||||
signal action_triggered(entry: __StepEntry, action: Actions)
|
||||
|
||||
const __EditLabel := preload("../../edit_label/edit_label.gd")
|
||||
const __StepData := preload("../../data/step.gd")
|
||||
const __Singletons := preload("../../plugin_singleton/singletons.gd")
|
||||
const __Shortcuts := preload("../shortcuts.gd")
|
||||
const __StepEntry := preload("step_entry.gd")
|
||||
|
||||
enum Actions {
|
||||
DELETE,
|
||||
MOVE_UP,
|
||||
MOVE_DOWN,
|
||||
EDIT_HARD, ## Forces the step details to open.
|
||||
EDIT_SOFT, ## Only switches to this step if the details are opened.
|
||||
CLOSE,
|
||||
}
|
||||
|
||||
@export var context_menu_enabled: bool = true
|
||||
|
||||
var done: CheckBox
|
||||
var title_label: Label
|
||||
var focus_box: StyleBoxFlat
|
||||
var context_menu: PopupMenu
|
||||
|
||||
var step_data: __StepData
|
||||
|
||||
var being_edited := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_h_size_flags(SIZE_EXPAND_FILL)
|
||||
|
||||
context_menu = PopupMenu.new()
|
||||
context_menu.id_pressed.connect(__action)
|
||||
add_child(context_menu)
|
||||
|
||||
done = CheckBox.new()
|
||||
done.focus_mode = Control.FOCUS_NONE
|
||||
done.toggled.connect(__set_done)
|
||||
add_child(done)
|
||||
|
||||
title_label = Label.new()
|
||||
title_label.set_h_size_flags(SIZE_EXPAND_FILL)
|
||||
title_label.text = step_data.details
|
||||
title_label.max_lines_visible = 1
|
||||
title_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
add_child(title_label)
|
||||
|
||||
focus_box = StyleBoxFlat.new()
|
||||
focus_box.bg_color = Color(1, 1, 1, 0.1)
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
step_data.changed.connect(update)
|
||||
update()
|
||||
|
||||
|
||||
func _shortcut_input(event: InputEvent) -> void:
|
||||
if not __Shortcuts.should_handle_shortcut(self):
|
||||
return
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
if not event.is_echo() and event.is_pressed():
|
||||
if shortcuts.rename.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
__action(Actions.EDIT_HARD)
|
||||
elif shortcuts.confirm.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
done.button_pressed = not done.button_pressed
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
accept_event()
|
||||
if context_menu_enabled:
|
||||
__update_context_menu()
|
||||
context_menu.position = get_global_mouse_position()
|
||||
if not get_window().gui_embed_subwindows:
|
||||
context_menu.position += get_window().position
|
||||
context_menu.popup()
|
||||
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed() and event.is_double_click():
|
||||
__action(Actions.EDIT_HARD)
|
||||
|
||||
|
||||
func _notification(what) -> void:
|
||||
match(what):
|
||||
NOTIFICATION_DRAW:
|
||||
if has_focus() or being_edited:
|
||||
focus_box.draw(get_canvas_item(), Rect2(Vector2.ZERO, get_rect().size))
|
||||
NOTIFICATION_FOCUS_ENTER:
|
||||
__action(Actions.EDIT_SOFT)
|
||||
|
||||
|
||||
func update() -> void:
|
||||
tooltip_text = step_data.details
|
||||
done.set_pressed_no_signal(step_data.done)
|
||||
|
||||
title_label.text = step_data.details
|
||||
|
||||
|
||||
func __action(what: Actions) -> void:
|
||||
action_triggered.emit(self, what)
|
||||
|
||||
|
||||
func __update_context_menu() -> void:
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
|
||||
context_menu.clear()
|
||||
context_menu.size = Vector2.ZERO
|
||||
|
||||
if being_edited:
|
||||
context_menu.add_icon_item(get_theme_icon(&"Close", &"EditorIcons"), "Close", Actions.CLOSE)
|
||||
else:
|
||||
context_menu.add_icon_item(get_theme_icon(&"Rename", &"EditorIcons"), "Edit", Actions.EDIT_HARD)
|
||||
context_menu.set_item_shortcut(context_menu.get_item_index(Actions.EDIT_HARD), shortcuts.rename)
|
||||
|
||||
context_menu.add_icon_item(get_theme_icon(&"MoveUp", &"EditorIcons"), "Move Up", Actions.MOVE_UP)
|
||||
context_menu.set_item_disabled(context_menu.get_item_index(Actions.MOVE_UP), get_index() == 0)
|
||||
context_menu.add_icon_item(get_theme_icon(&"MoveDown", &"EditorIcons"), "Move Down", Actions.MOVE_DOWN)
|
||||
context_menu.set_item_disabled(context_menu.get_item_index(Actions.MOVE_DOWN), get_index() == get_parent().get_child_count() - 1)
|
||||
|
||||
context_menu.add_separator()
|
||||
|
||||
context_menu.add_icon_item(get_theme_icon(&"Remove", &"EditorIcons"), "Delete", Actions.DELETE)
|
||||
|
||||
|
||||
func __set_done(done: bool) -> void:
|
||||
__action(Actions.CLOSE)
|
||||
step_data.done = done
|
||||
@@ -0,0 +1 @@
|
||||
uid://cdyn8h04v5qqh
|
||||
@@ -0,0 +1,190 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
signal entry_action_triggered(entry: __StepEntry, action: __StepEntry.Actions)
|
||||
signal entry_move_requesed(moved_entry: __StepEntry, target_entry: __StepEntry, move_after_target: bool)
|
||||
|
||||
const __StepData := preload("../../data/step.gd")
|
||||
const __StepEntry := preload("../details/step_entry.gd")
|
||||
|
||||
@export var scrollable: bool = true:
|
||||
set(value):
|
||||
if value != scrollable:
|
||||
scrollable = value
|
||||
__update_children_settings()
|
||||
|
||||
@export var steps_can_be_removed: bool = true:
|
||||
set(value):
|
||||
if value != steps_can_be_removed:
|
||||
steps_can_be_removed = value
|
||||
__update_children_settings()
|
||||
|
||||
@export var steps_can_be_reordered: bool = true:
|
||||
set(value):
|
||||
if value != steps_can_be_reordered:
|
||||
steps_can_be_reordered = value
|
||||
__update_children_settings()
|
||||
|
||||
@export var steps_have_context_menu: bool = true:
|
||||
set(value):
|
||||
if value != steps_have_context_menu:
|
||||
steps_have_context_menu = value
|
||||
__update_children_settings()
|
||||
|
||||
@export var steps_focus_mode := FocusMode.FOCUS_NONE:
|
||||
set(value):
|
||||
if value != steps_focus_mode:
|
||||
steps_focus_mode = value
|
||||
__update_children_settings()
|
||||
|
||||
var __mouse_entered_step_list: bool = false
|
||||
var __move_target_entry: __StepEntry = null
|
||||
var __move_after_target: bool = false
|
||||
|
||||
@onready var __scroll_container: ScrollContainer = %ScrollContainer
|
||||
@onready var __remove_separator: HSeparator = %RemoveSeparator
|
||||
@onready var __step_list: VBoxContainer = %StepList
|
||||
@onready var __remove_area: Button = %RemoveArea
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
__step_list.draw.connect(__on_step_list_draw)
|
||||
__step_list.mouse_exited.connect(__on_step_list_mouse_exited)
|
||||
__step_list.mouse_entered.connect(__on_step_list_mouse_entered)
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
__update_children_settings()
|
||||
|
||||
|
||||
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
|
||||
if not steps_can_be_removed and not steps_can_be_reordered:
|
||||
return false
|
||||
if data is __StepEntry:
|
||||
if __remove_area.get_global_rect().has_point(get_global_transform() * at_position):
|
||||
return true
|
||||
__update_move_target(at_position)
|
||||
return (__move_target_entry != null)
|
||||
return false
|
||||
|
||||
|
||||
func _get_drag_data(at_position: Vector2) -> Variant:
|
||||
if not steps_can_be_removed and not steps_can_be_reordered:
|
||||
return null
|
||||
for entry in get_step_entries():
|
||||
if entry.get_global_rect().has_point(get_global_transform() * at_position):
|
||||
var preview := Label.new()
|
||||
preview.text = entry.step_data.details
|
||||
set_drag_preview(preview)
|
||||
return entry
|
||||
return null
|
||||
|
||||
|
||||
func _drop_data(at_position: Vector2, data: Variant) -> void:
|
||||
if __move_target_entry != null:
|
||||
entry_move_requesed.emit(data, __move_target_entry, __move_after_target)
|
||||
__move_target_entry = null
|
||||
if data is __StepEntry:
|
||||
if __remove_area.get_global_rect().has_point(get_global_transform() * at_position):
|
||||
data.__action(__StepEntry.Actions.DELETE)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
if is_instance_valid(__remove_area):
|
||||
__remove_area.icon = get_theme_icon(&"Remove", &"EditorIcons")
|
||||
|
||||
|
||||
func add_step(step: __StepData) -> void:
|
||||
var entry = __StepEntry.new()
|
||||
entry.step_data = step
|
||||
entry.show_behind_parent = true
|
||||
entry.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
__step_list.add_child(entry)
|
||||
entry.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
entry.action_triggered.connect(__on_entry_action_triggered)
|
||||
entry.context_menu_enabled = steps_have_context_menu
|
||||
entry.focus_mode = steps_focus_mode
|
||||
|
||||
|
||||
func clear_steps() -> void:
|
||||
for step in get_step_entries():
|
||||
__step_list.remove_child(step)
|
||||
step.queue_free()
|
||||
|
||||
|
||||
func get_step_entries() -> Array[__StepEntry]:
|
||||
var step_entries: Array[__StepEntry] = []
|
||||
if is_instance_valid(__step_list):
|
||||
for child in __step_list.get_children():
|
||||
if child is __StepEntry:
|
||||
step_entries.append(child)
|
||||
return step_entries
|
||||
|
||||
|
||||
func __update_children_settings() -> void:
|
||||
if is_instance_valid(__scroll_container):
|
||||
__scroll_container.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO if scrollable else ScrollContainer.SCROLL_MODE_DISABLED
|
||||
if is_instance_valid(__remove_separator):
|
||||
__remove_separator.visible = steps_can_be_removed
|
||||
if is_instance_valid(__remove_area):
|
||||
__remove_area.visible = steps_can_be_removed
|
||||
for entry in get_step_entries():
|
||||
entry.context_menu_enabled = steps_have_context_menu
|
||||
entry.focus_mode = steps_focus_mode
|
||||
|
||||
|
||||
func __update_move_target(at_position: Vector2) -> void:
|
||||
var at_global_position := get_global_transform() * at_position
|
||||
# This __mouse_entered_step_list is needed here, as this seemed to be the only reliable solution, as:
|
||||
# 1) something is NOK with transforming at_position to global and compare with step_list.global_rect
|
||||
# 2) cannot decide what is the visible rect of the step_list
|
||||
# 3) _can_drop_data was called even after mouse is outside the list (to the bottom direction)
|
||||
if __mouse_entered_step_list:
|
||||
var closes_entry: __StepEntry = null
|
||||
var smallest_distance: float
|
||||
var position_is_after_closes_entry: bool
|
||||
for e in get_step_entries():
|
||||
var entry_global_rect = e.get_global_rect()
|
||||
var distance := abs(at_global_position.y - entry_global_rect.position.y)
|
||||
if closes_entry == null or distance < smallest_distance:
|
||||
closes_entry = e
|
||||
smallest_distance = distance
|
||||
position_is_after_closes_entry = false
|
||||
distance = abs(at_global_position.y - entry_global_rect.end.y)
|
||||
if closes_entry == null or distance < smallest_distance:
|
||||
closes_entry = e
|
||||
smallest_distance = distance
|
||||
position_is_after_closes_entry = true
|
||||
__move_target_entry = closes_entry
|
||||
__move_after_target = position_is_after_closes_entry
|
||||
else:
|
||||
__move_target_entry = null
|
||||
__step_list.queue_redraw()
|
||||
|
||||
|
||||
func __on_step_list_mouse_entered() -> void:
|
||||
__mouse_entered_step_list = true
|
||||
|
||||
|
||||
func __on_step_list_mouse_exited() -> void:
|
||||
__mouse_entered_step_list = false
|
||||
__update_move_target(get_local_mouse_position())
|
||||
|
||||
|
||||
func __on_step_list_draw() -> void:
|
||||
if __move_target_entry != null:
|
||||
var target_rect := __step_list.get_global_transform().inverse() * __move_target_entry.get_global_rect()
|
||||
var separation = __step_list.get_theme_constant(&"separation")
|
||||
var preview_rect := Rect2(
|
||||
Vector2(0, target_rect.end.y if __move_after_target else target_rect.position.y - separation),
|
||||
Vector2(target_rect.size.x, separation)
|
||||
)
|
||||
if preview_rect.position.y < 0:
|
||||
preview_rect.position.y = 0
|
||||
if preview_rect.end.y > __step_list.size.y:
|
||||
preview_rect.position.y -= (preview_rect.end.y - __step_list.size.y)
|
||||
__step_list.draw_rect(preview_rect, get_theme_color(&"step_move_review_color"))
|
||||
|
||||
|
||||
func __on_entry_action_triggered(entry, action) -> void:
|
||||
entry_action_triggered.emit(entry, action)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dw1afc1ntflyw
|
||||
@@ -0,0 +1,45 @@
|
||||
[gd_scene format=3 uid="uid://dwjg5vyxx4g48"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dw1afc1ntflyw" path="res://addons/kanban_tasks/view/details/step_holder.gd" id="1_exd17"]
|
||||
|
||||
[sub_resource type="Theme" id="Theme_1hs0w"]
|
||||
StepHolder/base_type = &"VBoxContainer"
|
||||
StepHolder/colors/step_move_review_color = Color(0.439216, 0.729412, 0.980392, 0.501961)
|
||||
|
||||
[node name="StepHolder" type="VBoxContainer" unique_id=1736962155]
|
||||
offset_right = 326.0
|
||||
offset_bottom = 500.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme = SubResource("Theme_1hs0w")
|
||||
theme_type_variation = &"StepHolder"
|
||||
script = ExtResource("1_exd17")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=1852819234]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
metadata/_edit_use_anchors_ = true
|
||||
|
||||
[node name="StepList" type="VBoxContainer" parent="ScrollContainer" unique_id=643748775]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="RemoveSeparator" type="HSeparator" parent="." unique_id=106663436]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="RemoveArea" type="Button" parent="." unique_id=1184154065]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 8
|
||||
focus_mode = 0
|
||||
mouse_filter = 2
|
||||
button_mask = 0
|
||||
flat = true
|
||||
icon_alignment = 1
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458 26.458" xmlns="http://www.w3.org/2000/svg"><g stroke-linecap="round" stroke-linejoin="round"><path d="m1.3229 1.3229h10.583v23.812h-10.583z" fill="#575b64" stroke="#575b64" stroke-width="1.0583"/><path d="m14.552 1.3229h10.583v23.812h-10.583z" fill="#626771" stroke="#626771" stroke-width="1.0583"/><path d="m2.3476 10.695h15.217v3.9785h-15.217z" transform="matrix(.99153-.12989.11858.99294 0 0)" fill="#179ceb" stroke="#179ceb" stroke-width="1.0584"/></g><path d="m16.933 10.848c-.9234.09187-1.7773.46826-2.9104 1.1617 1.1037-.18609 2.0649-.1914 3.4396.16123l-.52917-1.3229" fill="#a4c1d3" fill-opacity=".60851"/><path d="m16.931 10.715c-.016153.000299-.032539.003438-.048059.009819-.04982.02053-.082375.069113-.082165.12299v1.0588c.000615.08981.088821.15306.17415.12506l.22738-.075448.050642.15193a.1825.1825 0 00.23048.11524.1825.1825 0 00.11524-.23048l-.050126-.15141.22118-.073381c.09245-.03185.11961-.14979.050643-.21911l-.79272-.79478c-.02598-.026228-.061098-.03993-.096635-.039274z" fill="#d8d8d8" stroke-linejoin="round"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://3edv1ymvukp0"
|
||||
path="res://.godot/imported/1.svg-15461a63252c61d47c72a31629894a26.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/kanban_tasks/view/documentation/1.svg"
|
||||
dest_files=["res://.godot/imported/1.svg-15461a63252c61d47c72a31629894a26.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
svg/scale=4.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458 26.458" xmlns="http://www.w3.org/2000/svg"><g fill="#575b64" stroke="#575b64" stroke-linejoin="round"><path d="m1.3229 1.3229h10.583v23.812h-10.583z" stroke-width="1.0583"/><path d="m14.552 14.552h10.583v10.583h-10.583z" stroke-width="1.058"/><path d="m14.552 1.3229h10.583v10.583h-10.583z" stroke-width="1.0581"/></g><path d="m1.852 2.9104h9.5251v1.3229h-9.5251z" fill="#179ceb" stroke="#179ceb" stroke-width="1.0581" stroke-linejoin="round"/><path d="m2.1167 3.6049h7.9375" fill="none" stroke="#106da5" stroke-width=".79375" stroke-linecap="round"/><path d="m8.4073 3.5781-.23386-.80682.60804.65481.40926-.42095.10524.63142.42095.22217-.52619.070158-.22217.38587-.26894-.39756-.57296.15201z" fill="#afb7c9" fill-opacity=".84681"/><path d="m8.2585 3.454-.23386-.80682.60804.65481.40926-.42095.10524.63142.42095.22217-.52619.070158-.22217.38587-.26894-.39756-.57296.15201z" fill="#afb7c9" fill-opacity=".65957"/><path d="m8.8615 3.5711c-.016153.000299-.032539.0034-.048059.0098-.04982.02053-.082375.06911-.082165.12299v1.0588c.000615.08981.088821.15306.17415.12506l.22738-.07545.050642.15193a.1825.1825 0 00.23048.11524.1825.1825 0 00.11524-.23048l-.050126-.15141.22118-.07338c.09245-.03185.11961-.14979.050643-.21911l-.79272-.79478c-.02598-.02623-.061098-.03993-.096635-.03927z" fill="#d8d8d8"/></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bbo1gfac2wymg"
|
||||
path="res://.godot/imported/2.svg-053c5d997067871191f4000b81461e5d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/kanban_tasks/view/documentation/2.svg"
|
||||
dest_files=["res://.godot/imported/2.svg-053c5d997067871191f4000b81461e5d.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
svg/scale=4.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458 26.458" xmlns="http://www.w3.org/2000/svg"><g stroke-linecap="round" stroke-linejoin="round"><path d="m7.2099 4.0349h13.262v18.058h-13.262z" fill="#575b64" stroke="#575b64" stroke-width="1.0583"/><path d="m9.2603 22.49h9.5251v1.3229h-9.5251z" fill="#179ceb" stroke="#179ceb" stroke-width="1.0581"/><path d="m9.525 23.184h7.9375" fill="none" stroke="#106da5" stroke-width=".79375"/></g><circle cx="18.654" cy="23.184" r=".39464" fill="#106da5"/><path d="m18.362 23.157-.23386-.80682.60804.65481.40926-.42095.10524.63142.42095.22217-.52619.07016-.22217.38587-.26894-.39756-.57296.15201z" fill="#afb7c9" fill-opacity=".84681"/><path d="m18.816 23.15c-.01615.000299-.03254.0034-.04806.0098-.04982.02053-.08237.06911-.08216.12299v1.0588c.000615.08981.08882.15306.17415.12506l.22738-.07545.05064.15193a.1825.1825 0 00.23048.11524.1825.1825 0 00.11524-.23048l-.05013-.15141.22118-.07338c.09245-.03185.11961-.14979.05064-.21911l-.79271-.79478c-.02598-.02623-.0611-.03993-.09664-.03927z" fill="#d8d8d8"/><g fill="#646973" stroke="#646973" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.0583"><path d="m7.6729 4.653h12.336v1.1451h-12.336z"/><path d="m7.6729 7.828h8.2021v12.688h-8.2021z"/><path d="m17.793 7.828h2.2159v12.688h-2.2159z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bb8knc6dctfj1"
|
||||
path="res://.godot/imported/3.svg-456dac6515246348acd4893e98f4bdc7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/kanban_tasks/view/documentation/3.svg"
|
||||
dest_files=["res://.godot/imported/3.svg-456dac6515246348acd4893e98f4bdc7.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
svg/scale=4.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="100" height="100" version="1.1" viewBox="0 0 26.458 26.458" xmlns="http://www.w3.org/2000/svg"><path transform="translate(5.3909 6.057)" d="m7.8383-.63474c.61268 0 2.2256 4.4997 2.7212 4.8598.49567.36013 5.2736.50359 5.4629 1.0863.18933.5827-3.5917 3.5071-3.7811 4.0898-.18933.5827 1.1507 5.1711.65501 5.5312-.49567.36013-4.4454-2.3322-5.0581-2.3322s-4.5624 2.6923-5.0581 2.3322c-.49567-.36013.84434-4.9485.65501-5.5312-.18933-.5827-3.9704-3.5071-3.7811-4.0898.18933-.5827 4.9672-.72616 5.4629-1.0863.49567-.36013 2.1086-4.8598 2.7212-4.8598z" fill="#d0c268" stroke="#d0c268" stroke-width=".79375"/></svg>
|
||||
|
After Width: | Height: | Size: 617 B |
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://phpa3kr3tjwu"
|
||||
path="res://.godot/imported/4.svg-963688afa901818a04233dc49468c7c7.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/kanban_tasks/view/documentation/4.svg"
|
||||
dest_files=["res://.godot/imported/4.svg-963688afa901818a04233dc49468c7c7.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
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/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
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=0
|
||||
svg/scale=4.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,21 @@
|
||||
@tool
|
||||
extends AcceptDialog
|
||||
|
||||
|
||||
@onready var _shameless_plug: RichTextLabel = %ShamelessPlug
|
||||
|
||||
|
||||
const EDITOR_ONLY_PLACEHOLDER := "$EDITORONLY$"
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_shameless_plug.meta_clicked.connect(_on_shameless_plug_meta_clicked)
|
||||
if not Engine.is_editor_hint():
|
||||
_shameless_plug.text = _shameless_plug.text.substr(0, _shameless_plug.text.find(EDITOR_ONLY_PLACEHOLDER))
|
||||
else:
|
||||
_shameless_plug.text = _shameless_plug.text.replace(EDITOR_ONLY_PLACEHOLDER, "")
|
||||
|
||||
|
||||
func _on_shameless_plug_meta_clicked(meta: Variant) -> void:
|
||||
# Open clicked URLs.
|
||||
OS.shell_open(str(meta))
|
||||
@@ -0,0 +1 @@
|
||||
uid://c6ccwvv8tjdsk
|
||||
@@ -0,0 +1,125 @@
|
||||
[gd_scene format=3 uid="uid://cwfixtyy5lpin"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c6ccwvv8tjdsk" path="res://addons/kanban_tasks/view/documentation/documentation.gd" id="1_fw3gy"]
|
||||
[ext_resource type="Texture2D" uid="uid://3edv1ymvukp0" path="res://addons/kanban_tasks/view/documentation/1.svg" id="1_inmy7"]
|
||||
[ext_resource type="Texture2D" uid="uid://bbo1gfac2wymg" path="res://addons/kanban_tasks/view/documentation/2.svg" id="2_1g6ul"]
|
||||
[ext_resource type="Texture2D" uid="uid://bb8knc6dctfj1" path="res://addons/kanban_tasks/view/documentation/3.svg" id="3_e3hha"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_cmnk1"]
|
||||
font_size = 20
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_cmvuv"]
|
||||
|
||||
[node name="AcceptDialog" type="AcceptDialog" unique_id=1501486445]
|
||||
oversampling_override = 1.0
|
||||
title = "Documentation"
|
||||
size = Vector2i(1000, 600)
|
||||
min_size = Vector2i(1000, 600)
|
||||
theme_type_variation = &"EditorSettingsDialog"
|
||||
ok_button_text = "Close"
|
||||
script = ExtResource("1_fw3gy")
|
||||
|
||||
[node name="Help" type="ScrollContainer" parent="." unique_id=535755392]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Help" unique_id=1359404301]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="PanelContainer1" type="PanelContainer" parent="Help/VBoxContainer" unique_id=983778674]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"Panel"
|
||||
|
||||
[node name="HBoxContainer1" type="HBoxContainer" parent="Help/VBoxContainer/PanelContainer1" unique_id=861006902]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 50
|
||||
alignment = 2
|
||||
|
||||
[node name="Control" type="Control" parent="Help/VBoxContainer/PanelContainer1/HBoxContainer1" unique_id=1543101361]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Help/VBoxContainer/PanelContainer1/HBoxContainer1" unique_id=1613673530]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "A kanban board helps you to organise tasks. After you created a task you can drag and drop it between the stages to reflect its current status. Add spontaneous ideas to the \"Todo\" stage. Move them into \"Doing\" when you are ready to tackle them. Once a task is done move it into \"Done\" to keep track of all your accomplishments."
|
||||
label_settings = SubResource("LabelSettings_cmnk1")
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/PanelContainer1/HBoxContainer1" unique_id=892191990]
|
||||
custom_minimum_size = Vector2(200, 200)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("1_inmy7")
|
||||
expand_mode = 1
|
||||
|
||||
[node name="PanelContainer2" type="PanelContainer" parent="Help/VBoxContainer" unique_id=90515732]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"Panel"
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="Help/VBoxContainer/PanelContainer2" unique_id=1366977175]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 50
|
||||
alignment = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/PanelContainer2/HBoxContainer2" unique_id=698644339]
|
||||
custom_minimum_size = Vector2(200, 200)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("2_1g6ul")
|
||||
expand_mode = 1
|
||||
|
||||
[node name="Label" type="Label" parent="Help/VBoxContainer/PanelContainer2/HBoxContainer2" unique_id=1865995285]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Boost your productivity by customizing your board!
|
||||
Double click stage or task names to change them. Configure categories and change the layout in the settings.
|
||||
Find tasks by using the search bar."
|
||||
label_settings = SubResource("LabelSettings_cmnk1")
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="Control" type="Control" parent="Help/VBoxContainer/PanelContainer2/HBoxContainer2" unique_id=2057758449]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PanelContainer3" type="PanelContainer" parent="Help/VBoxContainer" unique_id=792363036]
|
||||
layout_mode = 2
|
||||
theme_type_variation = &"Panel"
|
||||
|
||||
[node name="HBoxContainer3" type="HBoxContainer" parent="Help/VBoxContainer/PanelContainer3" unique_id=242955631]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 50
|
||||
alignment = 2
|
||||
|
||||
[node name="Control" type="Control" parent="Help/VBoxContainer/PanelContainer3/HBoxContainer3" unique_id=1118352302]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Help/VBoxContainer/PanelContainer3/HBoxContainer3" unique_id=978285368]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Edit the details of you tasks by clicking the edit button. Give your task a meaningful title and put the details into the description. Give your task a category to keep the overview."
|
||||
label_settings = SubResource("LabelSettings_cmnk1")
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/PanelContainer3/HBoxContainer3" unique_id=125050301]
|
||||
custom_minimum_size = Vector2(200, 200)
|
||||
layout_mode = 2
|
||||
texture = ExtResource("3_e3hha")
|
||||
expand_mode = 1
|
||||
|
||||
[node name="ShamelessPlug" type="RichTextLabel" parent="Help/VBoxContainer" unique_id=2067203823]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/normal_font_size = 17
|
||||
theme_override_styles/normal = SubResource("StyleBoxEmpty_cmvuv")
|
||||
bbcode_enabled = true
|
||||
text = "[center][img height=2em]uid://phpa3kr3tjwu[/img] Leave a star on [url=https://github.com/HolonProduction/godot_kanban_tasks]Github[/url] or a like on the [url=https://store.godotengine.org/asset/holonproduction/kanban-tasks/]Asset Store[/url][/center]"
|
||||
fit_content = true
|
||||
scroll_active = false
|
||||
shortcut_keys_enabled = false
|
||||
@@ -0,0 +1,29 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
## Global stuff for the view system.
|
||||
|
||||
|
||||
const __Filter := preload("filter.gd")
|
||||
const __SettingData := preload("../data/settings.gd")
|
||||
|
||||
signal filter_changed()
|
||||
signal save_board()
|
||||
signal create_board()
|
||||
signal reload_board(discard_changes: bool)
|
||||
|
||||
## The currently active filter.
|
||||
var filter: __Filter = null:
|
||||
set(value):
|
||||
filter = value
|
||||
filter_changed.emit()
|
||||
|
||||
## The undo redo for task operations.
|
||||
var undo_redo := UndoRedo.new()
|
||||
|
||||
## uuid of the object that should have focus. This is used to persist focus
|
||||
## when updating some views.
|
||||
var focus: String = ""
|
||||
|
||||
## Settings that are not tied to the board.
|
||||
var settings := __SettingData.new()
|
||||
@@ -0,0 +1 @@
|
||||
uid://3e38f0mlpigu
|
||||
@@ -0,0 +1,14 @@
|
||||
@tool
|
||||
extends RefCounted
|
||||
|
||||
## A filter configuration for searching tasks.
|
||||
|
||||
|
||||
var text: String
|
||||
## Whether to search in descriptions.
|
||||
var advanced: bool
|
||||
|
||||
|
||||
func _init(p_text: String = "", p_advanced: bool = false) -> void:
|
||||
text = p_text
|
||||
advanced = p_advanced
|
||||
@@ -0,0 +1 @@
|
||||
uid://b8vdc7jbb0ocg
|
||||
@@ -0,0 +1,55 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
const __BoardData := preload("../../../data/board.gd")
|
||||
const __CategoryEntry := preload("../../settings/categories/category_entry.gd")
|
||||
const __CategoryData := preload("../../../data/category.gd")
|
||||
const __EditLabel := preload("../../../edit_label/edit_label.gd")
|
||||
|
||||
var board_data: __BoardData
|
||||
|
||||
var randomizer := RandomNumberGenerator.new()
|
||||
|
||||
@onready var category_holder: VBoxContainer = %CategoryHolder
|
||||
@onready var scroll_container: ScrollContainer = %ScrollContainer
|
||||
@onready var add_category_button: Button = %Add
|
||||
@onready var panel_container: PanelContainer = %PanelContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
randomizer.randomize()
|
||||
add_category_button.pressed.connect(__add_category)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
if is_instance_valid(add_category_button):
|
||||
add_category_button.icon = get_theme_icon(&"Add", &"EditorIcons")
|
||||
if is_instance_valid(panel_container):
|
||||
panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Panel"))
|
||||
|
||||
|
||||
func update() -> void:
|
||||
for category in category_holder.get_children():
|
||||
category.queue_free()
|
||||
|
||||
for uuid in board_data.get_categories():
|
||||
var entry := __CategoryEntry.new()
|
||||
entry.board_data = board_data
|
||||
entry.data_uuid = uuid
|
||||
|
||||
category_holder.add_child(entry)
|
||||
|
||||
|
||||
func __add_category() -> void:
|
||||
var color = Color.from_hsv(randomizer.randf(), randomizer.randf_range(0.8, 1.0), randomizer.randf_range(0.7, 1.0))
|
||||
var data = __CategoryData.new("New category", color)
|
||||
var uuid = board_data.add_category(data)
|
||||
update()
|
||||
for i in category_holder.get_children():
|
||||
if i.data_uuid == uuid:
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
i.grab_focus()
|
||||
i.show_edit(__EditLabel.INTENTION.REPLACE)
|
||||
@@ -0,0 +1 @@
|
||||
uid://brn3wr8cbs41k
|
||||
@@ -0,0 +1,40 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b2likgss81t0s"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://brn3wr8cbs41k" path="res://addons/kanban_tasks/view/settings/categories/categories.gd" id="1_n36ev"]
|
||||
|
||||
[node name="Categories" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
script = ExtResource("1_n36ev")
|
||||
|
||||
[node name="Header" type="HBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="\'Available Categories\'" type="Label" parent="Header"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Available Categories:"
|
||||
|
||||
[node name="Add" type="Button" parent="Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
follow_focus = true
|
||||
|
||||
[node name="CategoryHolder" type="VBoxContainer" parent="PanelContainer/ScrollContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
@@ -0,0 +1,94 @@
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
## Visual representation of a category.
|
||||
|
||||
|
||||
const __EditLabel := preload("../../../edit_label/edit_label.gd")
|
||||
const __BoardData := preload("../../../data/board.gd")
|
||||
const __Singletons := preload("../../../plugin_singleton/singletons.gd")
|
||||
const __Shortcuts := preload("../../shortcuts.gd")
|
||||
|
||||
var title: __EditLabel
|
||||
var delete: Button
|
||||
var color_picker: ColorPickerButton
|
||||
var focus_box: StyleBoxFlat
|
||||
|
||||
var board_data: __BoardData
|
||||
var data_uuid: String
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
set_h_size_flags(SIZE_EXPAND_FILL)
|
||||
focus_mode = FOCUS_ALL
|
||||
title = __EditLabel.new()
|
||||
title.set_h_size_flags(SIZE_EXPAND_FILL)
|
||||
title.text = board_data.get_category(data_uuid).title
|
||||
title.text_changed.connect(__on_title_changed)
|
||||
add_child(title)
|
||||
|
||||
color_picker = ColorPickerButton.new()
|
||||
color_picker.custom_minimum_size.x = 100
|
||||
color_picker.edit_alpha = false
|
||||
color_picker.color = board_data.get_category(data_uuid).color
|
||||
color_picker.focus_mode = Control.FOCUS_NONE
|
||||
color_picker.flat = true
|
||||
color_picker.popup_closed.connect(__on_color_changed)
|
||||
add_child(color_picker)
|
||||
|
||||
delete = Button.new()
|
||||
delete.focus_mode = FOCUS_NONE
|
||||
delete.flat = true
|
||||
delete.disabled = board_data.get_category_count() <= 1
|
||||
delete.pressed.connect(__on_delete)
|
||||
add_child(delete)
|
||||
|
||||
focus_box = StyleBoxFlat.new()
|
||||
focus_box.bg_color = Color(1, 1, 1, 0.1)
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
|
||||
func _shortcut_input(event: InputEvent) -> void:
|
||||
if not __Shortcuts.should_handle_shortcut(self):
|
||||
return
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
if not event.is_echo() and event.is_pressed():
|
||||
if shortcuts.rename.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
title.show_edit()
|
||||
|
||||
|
||||
func _notification(what) -> void:
|
||||
match(what):
|
||||
NOTIFICATION_THEME_CHANGED when not is_part_of_edited_scene():
|
||||
if is_instance_valid(delete):
|
||||
delete.icon = get_theme_icon(&"Remove", &"EditorIcons")
|
||||
NOTIFICATION_DRAW:
|
||||
if has_focus():
|
||||
focus_box.draw(get_canvas_item(), Rect2(Vector2.ZERO, get_rect().size))
|
||||
|
||||
|
||||
func show_edit(intention: int = title.default_intention) -> void:
|
||||
title.show_edit(intention)
|
||||
|
||||
|
||||
func __on_title_changed(new: String) -> void:
|
||||
board_data.get_category(data_uuid).title = new
|
||||
|
||||
|
||||
func __on_color_changed() -> void:
|
||||
board_data.get_category(data_uuid).color = color_picker.color
|
||||
# Hack to get the tasks to update their color.
|
||||
board_data.layout.changed.emit()
|
||||
|
||||
|
||||
func __on_delete() -> void:
|
||||
board_data.remove_category(data_uuid)
|
||||
|
||||
var fallback_to = board_data.get_categories()[0]
|
||||
for uuid in board_data.get_tasks():
|
||||
if board_data.get_task(uuid).category == data_uuid:
|
||||
board_data.get_task(uuid).category = fallback_to
|
||||
|
||||
get_parent().get_owner().update()
|
||||
@@ -0,0 +1 @@
|
||||
uid://dlf6mdc2al4qm
|
||||
@@ -0,0 +1,150 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
const __Singletons := preload("../../../plugin_singleton/singletons.gd")
|
||||
const __EditContext := preload("../../edit_context.gd")
|
||||
const __SettingData := preload("../../../data/settings.gd")
|
||||
|
||||
var data: __SettingData = null
|
||||
|
||||
var file_dialog_open_option: CheckBox
|
||||
var file_dialog_save_option: CheckBox
|
||||
var file_dialog_create_option: CheckBox
|
||||
var file_dialog_option_button_group: ButtonGroup
|
||||
|
||||
@onready var show_description_preview: CheckBox = %ShowDescriptionPreview
|
||||
@onready var show_steps_preview: CheckBox = %ShowStepsPreview
|
||||
@onready var show_category_on_board: CheckBox = %ShowCategoriesOnBoard
|
||||
@onready var edit_step_details_exclusively: CheckBox = %EditStepDetailsExclusively
|
||||
@onready var max_displayed_lines_in_description: SpinBox = %MaxDisplayedLinesInDescription
|
||||
@onready var description_on_board: OptionButton = %DescriptionOnBoard
|
||||
# Keep IDs of the items of StepsOnBoard in sync with the values of setting.gd/StepsOnBoard
|
||||
@onready var steps_on_board: OptionButton = %StepsOnBoard
|
||||
@onready var max_steps_on_board: SpinBox = %MaxStepsOnBoard
|
||||
@onready var data_file_path_label: Control = %DataFilePathLabel
|
||||
@onready var data_file_path_container: Control = %DataFilePathContainer
|
||||
@onready var data_file_path: LineEdit = %DataFilePath
|
||||
@onready var data_file_path_button: Button = %DataFilePathButton
|
||||
@onready var file_dialog: FileDialog = %FileDialog
|
||||
@onready var panel_container: PanelContainer = %PanelContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
data = ctx.settings
|
||||
data.changed.connect(update)
|
||||
update()
|
||||
|
||||
show_description_preview.toggled.connect(func(x): __apply_changes())
|
||||
show_steps_preview.toggled.connect(func(x): __apply_changes())
|
||||
show_category_on_board.toggled.connect(func(x): __apply_changes())
|
||||
edit_step_details_exclusively.toggled.connect(func(x): __apply_changes())
|
||||
max_displayed_lines_in_description.value_changed.connect(func(x): __apply_changes())
|
||||
description_on_board.item_selected.connect(func(x): __apply_changes())
|
||||
steps_on_board.item_selected.connect(func(x): __apply_changes())
|
||||
max_steps_on_board.value_changed.connect(func(x): __apply_changes())
|
||||
if not Engine.is_editor_hint():
|
||||
data_file_path_label.visible = false
|
||||
data_file_path_container.visible = false
|
||||
data_file_path_button.pressed.connect(__open_data_file_path_dialog)
|
||||
|
||||
file_dialog_open_option = CheckBox.new()
|
||||
file_dialog_open_option.text = "Open board from existing file"
|
||||
file_dialog.get_vbox().add_child(file_dialog_open_option)
|
||||
file_dialog_save_option = CheckBox.new()
|
||||
file_dialog_save_option.text = "Save current board to file"
|
||||
file_dialog.get_vbox().add_child(file_dialog_save_option)
|
||||
file_dialog_create_option = CheckBox.new()
|
||||
file_dialog_create_option.text = "Create new board in file"
|
||||
file_dialog.get_vbox().add_child(file_dialog_create_option)
|
||||
file_dialog_option_button_group = ButtonGroup.new()
|
||||
file_dialog_open_option.button_group = file_dialog_option_button_group
|
||||
file_dialog_save_option.button_group = file_dialog_option_button_group
|
||||
file_dialog_create_option.button_group = file_dialog_option_button_group
|
||||
file_dialog_option_button_group.pressed.connect(func (button): __update_file_dialog())
|
||||
file_dialog.get_line_edit().text_changed.connect(func (new_text): __update_file_dialog())
|
||||
file_dialog_open_option.button_pressed = true
|
||||
|
||||
file_dialog.file_selected.connect(__update_editor_data_file)
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
if is_instance_valid(panel_container):
|
||||
panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Panel"))
|
||||
|
||||
|
||||
func update() -> void:
|
||||
show_description_preview.button_pressed = data.show_description_preview
|
||||
show_steps_preview.button_pressed = data.show_steps_preview
|
||||
show_category_on_board.button_pressed = data.show_category_on_board
|
||||
edit_step_details_exclusively.button_pressed = data.edit_step_details_exclusively
|
||||
max_displayed_lines_in_description.value = data.max_displayed_lines_in_description
|
||||
max_steps_on_board.value = data.max_steps_on_board
|
||||
|
||||
description_on_board.select(description_on_board.get_item_index(data.description_on_board))
|
||||
steps_on_board.select(steps_on_board.get_item_index(data.steps_on_board))
|
||||
|
||||
data_file_path.text = data.editor_data_file_path
|
||||
|
||||
|
||||
func __open_data_file_path_dialog() -> void:
|
||||
file_dialog_open_option.set_pressed_no_signal(true)
|
||||
file_dialog_save_option.set_pressed_no_signal(false)
|
||||
file_dialog_create_option.set_pressed_no_signal(false)
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.clear_filters()
|
||||
file_dialog.add_filter("*.kanban, *.json", "Kanban Board")
|
||||
file_dialog.popup_centered(file_dialog.size)
|
||||
|
||||
|
||||
func __update_file_dialog() -> void:
|
||||
if file_dialog_save_option.button_pressed:
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
|
||||
file_dialog.title = file_dialog_save_option.text
|
||||
file_dialog.ok_button_text = "Save"
|
||||
elif file_dialog_create_option.button_pressed:
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
|
||||
file_dialog.title = file_dialog_create_option.text
|
||||
file_dialog.ok_button_text = "Create"
|
||||
else:
|
||||
file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
|
||||
file_dialog.title = file_dialog_open_option.text
|
||||
file_dialog.ok_button_text = "Open"
|
||||
|
||||
|
||||
func __update_editor_data_file(path: String) -> void:
|
||||
data_file_path.text = path
|
||||
__apply_changes()
|
||||
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
if file_dialog_save_option.button_pressed:
|
||||
ctx.save_board.emit()
|
||||
elif file_dialog_create_option.button_pressed:
|
||||
ctx.create_board.emit()
|
||||
else:
|
||||
ctx.reload_board.emit(false)
|
||||
|
||||
|
||||
func __apply_changes() -> void:
|
||||
if data.changed.is_connected(update):
|
||||
data.changed.disconnect(update)
|
||||
|
||||
data.__emit_changed = false
|
||||
data.show_description_preview = show_description_preview.button_pressed
|
||||
data.show_steps_preview = show_steps_preview.button_pressed
|
||||
data.show_category_on_board = show_category_on_board.button_pressed
|
||||
data.edit_step_details_exclusively = edit_step_details_exclusively.button_pressed
|
||||
data.max_displayed_lines_in_description = max_displayed_lines_in_description.value
|
||||
data.description_on_board = description_on_board.get_selected_id()
|
||||
data.steps_on_board = steps_on_board.get_selected_id()
|
||||
data.max_steps_on_board = max_steps_on_board.value
|
||||
data.editor_data_file_path = data_file_path.text
|
||||
data.__emit_changed = true
|
||||
data.__notify_changed()
|
||||
|
||||
data.changed.connect(update)
|
||||
@@ -0,0 +1 @@
|
||||
uid://nnqalkr7jnvx
|
||||
@@ -0,0 +1,157 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://due07vdflx4o"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://nnqalkr7jnvx" path="res://addons/kanban_tasks/view/settings/general/general.gd" id="1_8tblh"]
|
||||
|
||||
[node name="General" type="VBoxContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_8tblh")
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="GridContainer" type="GridContainer" parent="PanelContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
columns = 2
|
||||
|
||||
[node name="ShowDescriptionPreviewLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show description preview"
|
||||
|
||||
[node name="ShowDescriptionPreview" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 0
|
||||
text = "On"
|
||||
|
||||
[node name="ShowStepsPreviewLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show steps preview"
|
||||
|
||||
[node name="ShowStepsPreview" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 0
|
||||
text = "On"
|
||||
|
||||
[node name="ShowCategoriesOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Show categories on board"
|
||||
|
||||
[node name="ShowCategoriesOnBoard" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 0
|
||||
text = "On"
|
||||
|
||||
[node name="EditStepDetailsExclusivelyLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Edit step details in fullscreen"
|
||||
|
||||
[node name="EditStepDetailsExclusively" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 0
|
||||
text = "On"
|
||||
|
||||
[node name="DescriptionOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Description on board"
|
||||
|
||||
[node name="DescriptionOnBoard" type="OptionButton" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
selected = 1
|
||||
item_count = 3
|
||||
popup/item_0/text = "Full description"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "First line of description"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "Until first blank line of description"
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="MaxDisplayedLinesInDescriptionLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Maximum displayed lines in description"
|
||||
|
||||
[node name="MaxDisplayedLinesInDescription" type="SpinBox" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
allow_greater = true
|
||||
|
||||
[node name="StepsOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
text = "Steps on board"
|
||||
|
||||
[node name="StepsOnBoard" type="OptionButton" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
selected = 0
|
||||
item_count = 3
|
||||
popup/item_0/text = "Only open steps"
|
||||
popup/item_0/id = 0
|
||||
popup/item_1/text = "All, but open first"
|
||||
popup/item_1/id = 1
|
||||
popup/item_2/text = "All in original order"
|
||||
popup/item_2/id = 2
|
||||
|
||||
[node name="MaxStepsOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Maximum steps on board"
|
||||
|
||||
[node name="MaxStepsOnBoard" type="SpinBox" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
value = 2.0
|
||||
allow_greater = true
|
||||
|
||||
[node name="DataFilePathLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Data file path"
|
||||
|
||||
[node name="DataFilePathContainer" type="HBoxContainer" parent="PanelContainer/ScrollContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="DataFilePath" type="LineEdit" parent="PanelContainer/ScrollContainer/GridContainer/DataFilePathContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "res://kanban_tasks_data.kanban"
|
||||
editable = false
|
||||
|
||||
[node name="DataFilePathButton" type="Button" parent="PanelContainer/ScrollContainer/GridContainer/DataFilePathContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = " ... "
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Open board from existing file"
|
||||
size = Vector2i(800, 600)
|
||||
ok_button_text = "Open"
|
||||
mode_overrides_title = false
|
||||
file_mode = 0
|
||||
@@ -0,0 +1,34 @@
|
||||
@tool
|
||||
extends AcceptDialog
|
||||
|
||||
|
||||
const __BoardData := preload("../../data/board.gd")
|
||||
const __CategoriesScene := preload("../settings/categories/categories.tscn")
|
||||
const __CategoriesScript := preload("../settings/categories/categories.gd")
|
||||
|
||||
@onready var category_settings: __CategoriesScript = %Categories
|
||||
@onready var stage_settings = %Stages
|
||||
@onready var tab_container: TabContainer = %Settings
|
||||
|
||||
var board_data: __BoardData
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
# Wait for board to set board_data.
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
|
||||
category_settings.board_data = board_data
|
||||
stage_settings.board_data = board_data
|
||||
about_to_popup.connect(stage_settings.update)
|
||||
about_to_popup.connect(category_settings.update)
|
||||
|
||||
|
||||
# Workaround for godotengine/godot#70451
|
||||
func popup_centered_ratio_no_fullscreen(ratio: float = 0.8) -> void:
|
||||
var viewport: Viewport = get_parent().get_viewport()
|
||||
popup(Rect2i(Vector2(viewport.position) + viewport.size / 2.0 - viewport.size * ratio / 2.0, viewport.size * ratio))
|
||||
|
||||
|
||||
func on_theme_changed():
|
||||
# Called from parent since changing this during this nodes theme change notification will create infinite recursion.
|
||||
add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"EditorSettingsDialog"))
|
||||
@@ -0,0 +1 @@
|
||||
uid://7jskmn63x3pt
|
||||
@@ -0,0 +1,46 @@
|
||||
[gd_scene format=3 uid="uid://dh1yunmhipirg"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://7jskmn63x3pt" path="res://addons/kanban_tasks/view/settings/settings.gd" id="1_4eaw3"]
|
||||
[ext_resource type="PackedScene" uid="uid://due07vdflx4o" path="res://addons/kanban_tasks/view/settings/general/general.tscn" id="1_dk7pg"]
|
||||
[ext_resource type="PackedScene" uid="uid://b2likgss81t0s" path="res://addons/kanban_tasks/view/settings/categories/categories.tscn" id="3_iycb0"]
|
||||
[ext_resource type="PackedScene" uid="uid://dapkpnkm8sow8" path="res://addons/kanban_tasks/view/settings/stages/stages.tscn" id="4_okolg"]
|
||||
|
||||
[node name="Settings" type="AcceptDialog" unique_id=650180224]
|
||||
oversampling_override = 1.0
|
||||
title = "Settings"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(800, 400)
|
||||
visible = true
|
||||
script = ExtResource("1_4eaw3")
|
||||
|
||||
[node name="Settings" type="TabContainer" parent="." unique_id=690004289]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(600, 300)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_type_variation = &"TabContainerOdd"
|
||||
current_tab = 0
|
||||
|
||||
[node name="General" parent="Settings" unique_id=2109241999 instance=ExtResource("1_dk7pg")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="Categories" parent="Settings" unique_id=1569239372 instance=ExtResource("3_iycb0")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[node name="Stages" parent="Settings" unique_id=152864210 instance=ExtResource("4_okolg")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 2
|
||||
@@ -0,0 +1,205 @@
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
const __Singletons := preload("../../../plugin_singleton/singletons.gd")
|
||||
const __EditContext := preload("../../edit_context.gd")
|
||||
const __BoardData = preload("../../../data/board.gd")
|
||||
const __StageData = preload("../../../data/stage.gd")
|
||||
|
||||
var board_data: __BoardData
|
||||
|
||||
var stylebox_n: StyleBoxFlat
|
||||
var stylebox_hp: StyleBoxFlat
|
||||
|
||||
@onready var column_holder: HBoxContainer = %ColumnHolder
|
||||
@onready var column_add: Button = %AddColumn
|
||||
@onready var warning_sign: Button = %WarningSign
|
||||
@onready var warn_about_empty_deletion: CheckBox = %WarnAboutEmptyDeletion
|
||||
@onready var width_spin_box: SpinBox = %WidthSpinBox
|
||||
@onready var confirm_not_empty: ConfirmationDialog = %ConfirmNotEmpty
|
||||
@onready var confirm_empty: ConfirmationDialog = %ConfirmEmpty
|
||||
@onready var task_destination: OptionButton = %TaskDestination
|
||||
@onready var panel_container: PanelContainer = %PanelContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
column_add.focus_mode = Control.FOCUS_NONE
|
||||
column_add.pressed.connect(__on_add_stage.bind(-1))
|
||||
|
||||
var center_container := CenterContainer.new()
|
||||
center_container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
center_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
column_add.add_child(center_container)
|
||||
|
||||
var plus := TextureRect.new()
|
||||
plus.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
center_container.add_child(plus)
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.changed.connect(__settings_changed)
|
||||
|
||||
warn_about_empty_deletion.toggled.connect(__apply_settings_changes)
|
||||
width_spin_box.value_changed.connect(__apply_settings_changes)
|
||||
|
||||
|
||||
func _notification(what) -> void:
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
stylebox_n = get_theme_stylebox(&"normal", &"Button").duplicate()
|
||||
stylebox_n.set_border_width_all(1)
|
||||
stylebox_n.border_color = Color8(32, 32, 32, 255)
|
||||
|
||||
stylebox_hp = get_theme_stylebox(&"read_only", &"LineEdit").duplicate()
|
||||
stylebox_hp.set_border_width_all(1)
|
||||
stylebox_hp.border_color = Color8(32, 32, 32, 128)
|
||||
|
||||
if is_instance_valid(column_add):
|
||||
column_add.get_child(0).get_child(0).texture = get_theme_icon(&"Add", &"EditorIcons")
|
||||
column_add.add_theme_stylebox_override(&"normal", stylebox_n)
|
||||
column_add.add_theme_stylebox_override(&"hover", stylebox_hp)
|
||||
column_add.add_theme_stylebox_override(&"pressed", stylebox_hp)
|
||||
if is_instance_valid(warning_sign):
|
||||
warning_sign.icon = get_theme_icon(&"NodeWarning", &"EditorIcons")
|
||||
if is_instance_valid(panel_container):
|
||||
panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Panel"))
|
||||
|
||||
|
||||
func update() -> void:
|
||||
if not board_data.layout.changed.is_connected(update):
|
||||
board_data.layout.changed.connect(update)
|
||||
|
||||
var too_high = false
|
||||
for column in board_data.layout.columns:
|
||||
if len(column) > 3:
|
||||
too_high = true
|
||||
warning_sign.visible = too_high or len(board_data.layout.columns) > 4
|
||||
|
||||
for child in column_holder.get_children():
|
||||
child.queue_free()
|
||||
|
||||
var index = 0
|
||||
for column in board_data.layout.columns:
|
||||
var column_entry := VBoxContainer.new()
|
||||
column_entry.add_theme_constant_override(&"separation", 5)
|
||||
column_holder.add_child(column_entry)
|
||||
|
||||
for stage in column:
|
||||
var stage_entry := Button.new()
|
||||
stage_entry.tooltip_text = board_data.get_stage(stage).title
|
||||
stage_entry.focus_mode = Control.FOCUS_NONE
|
||||
stage_entry.set_v_size_flags(SIZE_EXPAND_FILL)
|
||||
stage_entry.custom_minimum_size = Vector2i(70, 50)
|
||||
stage_entry.add_theme_stylebox_override(&"normal", stylebox_n)
|
||||
stage_entry.add_theme_stylebox_override(&"hover", stylebox_hp)
|
||||
stage_entry.add_theme_stylebox_override(&"pressed", stylebox_hp)
|
||||
stage_entry.add_theme_stylebox_override(&"disabled", stylebox_hp)
|
||||
stage_entry.pressed.connect(__on_remove_stage.bind(stage))
|
||||
stage_entry.disabled = len(board_data.layout.columns) <= 1 and len(board_data.layout.columns[0]) <= 1
|
||||
column_entry.add_child(stage_entry)
|
||||
|
||||
var center_container := CenterContainer.new()
|
||||
center_container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
center_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
stage_entry.add_child(center_container)
|
||||
|
||||
var remove := TextureRect.new()
|
||||
remove.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
remove.texture = get_theme_icon(&"Remove", &"EditorIcons")
|
||||
center_container.add_child(remove)
|
||||
|
||||
var add = Button.new()
|
||||
add.custom_minimum_size = Vector2i(70, 40)
|
||||
add.focus_mode = Control.FOCUS_NONE
|
||||
add.pressed.connect(__on_add_stage.bind(index))
|
||||
add.add_theme_stylebox_override(&"normal", stylebox_n)
|
||||
add.add_theme_stylebox_override(&"hover", stylebox_hp)
|
||||
add.add_theme_stylebox_override(&"pressed", stylebox_hp)
|
||||
column_entry.add_child(add)
|
||||
|
||||
var center_container = CenterContainer.new()
|
||||
center_container.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||
center_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
add.add_child(center_container)
|
||||
|
||||
var plus := TextureRect.new()
|
||||
plus.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
center_container.add_child(plus)
|
||||
plus.texture = get_theme_icon(&"Add", &"EditorIcons")
|
||||
|
||||
index += 1
|
||||
|
||||
|
||||
func __on_add_stage(column: int) -> void:
|
||||
var data = __StageData.new("New Stage")
|
||||
var uuid = board_data.add_stage(data, true)
|
||||
|
||||
var columns = board_data.layout.columns
|
||||
if column < len(board_data.layout.columns) and column >= 0:
|
||||
columns[column].append(uuid)
|
||||
else:
|
||||
columns.append(PackedStringArray([uuid]))
|
||||
board_data.layout.columns = columns
|
||||
|
||||
|
||||
func __on_remove_stage(uuid: String) -> void:
|
||||
if len(board_data.get_stage(uuid).tasks) == 0:
|
||||
if warn_about_empty_deletion.button_pressed:
|
||||
if confirm_empty.confirmed.is_connected(__remove_stage):
|
||||
confirm_empty.confirmed.disconnect(__remove_stage)
|
||||
confirm_empty.confirmed.connect(__remove_stage.bind(uuid))
|
||||
confirm_empty.popup_centered()
|
||||
else:
|
||||
__remove_stage(uuid)
|
||||
else:
|
||||
__update_task_destination(uuid)
|
||||
if confirm_not_empty.confirmed.is_connected(__remove_stage):
|
||||
confirm_not_empty.confirmed.disconnect(__remove_stage)
|
||||
confirm_not_empty.confirmed.connect(__remove_stage.bind(uuid))
|
||||
confirm_not_empty.popup_centered()
|
||||
|
||||
|
||||
func __update_task_destination(uuid: String) -> void:
|
||||
task_destination.clear()
|
||||
for stage in board_data.get_stages():
|
||||
if stage != uuid:
|
||||
task_destination.add_item(board_data.get_stage(stage).title)
|
||||
task_destination.set_item_metadata(-1, stage)
|
||||
|
||||
|
||||
func __remove_stage(uuid: String) -> void:
|
||||
if len(board_data.get_stage(uuid).tasks) > 0:
|
||||
var old_tasks = board_data.get_stage(uuid).tasks
|
||||
var new_tasks = board_data.get_stage(task_destination.get_selected_metadata()).tasks
|
||||
for task in old_tasks.duplicate():
|
||||
old_tasks.erase(task)
|
||||
new_tasks.append(task)
|
||||
board_data.get_stage(uuid).tasks = old_tasks
|
||||
board_data.get_stage(task_destination.get_selected_metadata()).tasks = new_tasks
|
||||
|
||||
board_data.remove_stage(uuid, true)
|
||||
|
||||
var columns = board_data.layout.columns
|
||||
for column in columns.duplicate():
|
||||
if uuid in column:
|
||||
column.remove_at(column.find(uuid))
|
||||
if len(column) == 0:
|
||||
columns.erase(column)
|
||||
|
||||
board_data.layout.columns = columns
|
||||
|
||||
|
||||
func __settings_changed() -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
warn_about_empty_deletion.button_pressed = ctx.settings.warn_about_empty_deletion
|
||||
width_spin_box.value = ctx.settings.stages_width
|
||||
|
||||
|
||||
func __apply_settings_changes(warn: bool) -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.changed.disconnect(__settings_changed)
|
||||
ctx.settings.warn_about_empty_deletion = warn
|
||||
ctx.settings.stages_width = int(width_spin_box.value)
|
||||
ctx.settings.changed.connect(__settings_changed)
|
||||
@@ -0,0 +1 @@
|
||||
uid://kwwmdo325o42
|
||||
@@ -0,0 +1,138 @@
|
||||
[gd_scene format=3 uid="uid://dapkpnkm8sow8"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://kwwmdo325o42" path="res://addons/kanban_tasks/view/settings/stages/stages.gd" id="1_1yycq"]
|
||||
|
||||
[node name="Stages" type="VBoxContainer" unique_id=730317252]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_1yycq")
|
||||
|
||||
[node name="Header" type="HBoxContainer" parent="." unique_id=560617268]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="\'EditStageLayout\'" type="Label" parent="Header" unique_id=348522360]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Edit Stage Layout:"
|
||||
|
||||
[node name="\'StageWidth\'" type="Label" parent="Header" unique_id=827952690]
|
||||
layout_mode = 2
|
||||
text = "Stage Width"
|
||||
|
||||
[node name="WidthSpinBox" type="SpinBox" parent="Header" unique_id=2023771662]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 2
|
||||
accessibility_labeled_by_nodes = Array[NodePath]([NodePath("../\'StageWidth\'")])
|
||||
min_value = 200.0
|
||||
max_value = 800.0
|
||||
value = 200.0
|
||||
rounded = true
|
||||
allow_greater = true
|
||||
alignment = 1
|
||||
suffix = "px"
|
||||
select_all_on_focus = true
|
||||
|
||||
[node name="WarnAboutEmptyDeletion" type="CheckBox" parent="Header" unique_id=1017071750]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
text = "Warn about empty deletion."
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="." unique_id=1613118127]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer" unique_id=2013896487]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="PanelContainer/ScrollContainer" unique_id=1647933709]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Grid" type="HBoxContainer" parent="PanelContainer/ScrollContainer/CenterContainer" unique_id=1695163191]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="ColumnHolder" type="HBoxContainer" parent="PanelContainer/ScrollContainer/CenterContainer/Grid" unique_id=1263007802]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="AddColumn" type="VBoxContainer" parent="PanelContainer/ScrollContainer/CenterContainer/Grid" unique_id=2093623661]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="AddColumn" type="Button" parent="PanelContainer/ScrollContainer/CenterContainer/Grid/AddColumn" unique_id=1445148106]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(40, 105)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 0
|
||||
|
||||
[node name="Empty" type="Button" parent="PanelContainer/ScrollContainer/CenterContainer/Grid/AddColumn" unique_id=1868106499]
|
||||
self_modulate = Color(1, 1, 1, 0)
|
||||
custom_minimum_size = Vector2(40, 40)
|
||||
layout_mode = 2
|
||||
text = "+"
|
||||
|
||||
[node name="Warning" type="Control" parent="PanelContainer" unique_id=1339112746]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="WarningSign" type="Button" parent="PanelContainer/Warning" unique_id=1578402273]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
anchor_left = 1.0
|
||||
anchor_right = 1.0
|
||||
grow_horizontal = 0
|
||||
tooltip_text = "Adding to much stages can overflow the editor.
|
||||
|
||||
Recommended maximum: 4*3"
|
||||
focus_mode = 0
|
||||
flat = true
|
||||
|
||||
[node name="ConfirmNotEmpty" type="ConfirmationDialog" parent="." unique_id=1240101809]
|
||||
unique_name_in_owner = true
|
||||
title = "Delete Stage"
|
||||
size = Vector2i(403, 159)
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="ConfirmNotEmpty" unique_id=1173550062]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Label" type="Label" parent="ConfirmNotEmpty/VBoxContainer" unique_id=1210268278]
|
||||
layout_mode = 2
|
||||
text = "You are deleting a stage which has tasks assigned.
|
||||
|
||||
The tasks will be assigned to the following stage:"
|
||||
|
||||
[node name="TaskDestination" type="OptionButton" parent="ConfirmNotEmpty/VBoxContainer" unique_id=1622670225]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="ConfirmEmpty" type="ConfirmationDialog" parent="." unique_id=1107424921]
|
||||
unique_name_in_owner = true
|
||||
title = "Delete Stage"
|
||||
size = Vector2i(316, 100)
|
||||
dialog_text = "Do you really want to delete this stage?
|
||||
You can not undo this."
|
||||
@@ -0,0 +1,73 @@
|
||||
@tool
|
||||
extends Node
|
||||
|
||||
|
||||
var delete := Shortcut.new()
|
||||
var duplicate := Shortcut.new()
|
||||
var create := Shortcut.new()
|
||||
var rename := Shortcut.new()
|
||||
var search := Shortcut.new()
|
||||
var confirm := Shortcut.new()
|
||||
var undo := Shortcut.new()
|
||||
var redo := Shortcut.new()
|
||||
|
||||
var save := Shortcut.new()
|
||||
var save_as := Shortcut.new()
|
||||
|
||||
|
||||
## Returns whether a specific node should handle the shortcut.
|
||||
static func should_handle_shortcut(node: Node) -> bool:
|
||||
var focus_owner := node.get_viewport().gui_get_focus_owner()
|
||||
return focus_owner and (node.is_ancestor_of(focus_owner) or focus_owner == node)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
# TODO: Update on editor settings change.
|
||||
__update_shortcuts_editor()
|
||||
else:
|
||||
__update_shortcuts_standalone()
|
||||
|
||||
|
||||
func __update_shortcuts_editor() -> void:
|
||||
var editor_settings = Engine.get_singleton(&"EditorInterface").get_editor_settings()
|
||||
delete = editor_settings.get_shortcut("scene_tree/delete")
|
||||
duplicate = editor_settings.get_shortcut("scene_tree/duplicate")
|
||||
create = editor_settings.get_shortcut("scene_tree/add_child_node")
|
||||
rename = editor_settings.get_shortcut("scene_tree/rename")
|
||||
search = editor_settings.get_shortcut("editor/open_search")
|
||||
confirm = editor_settings.get_shortcut("ui_accept")
|
||||
undo = editor_settings.get_shortcut("ui_undo")
|
||||
redo = editor_settings.get_shortcut("ui_redo")
|
||||
|
||||
|
||||
func __create(key: Key, shift: bool, ctrl: bool):
|
||||
var ev := InputEventKey.new()
|
||||
ev.command_or_control_autoremap = ctrl
|
||||
ev.shift_pressed = shift
|
||||
ev.keycode = key
|
||||
var shortcut := Shortcut.new()
|
||||
shortcut.events.append(ev)
|
||||
return shortcut
|
||||
|
||||
|
||||
func __get(action: String, fallback: Shortcut = null) -> Shortcut:
|
||||
if InputMap.has_action(action):
|
||||
var shortcut := Shortcut.new()
|
||||
shortcut.events = InputMap.action_get_events(action)
|
||||
return shortcut
|
||||
return fallback
|
||||
|
||||
|
||||
func __update_shortcuts_standalone() -> void:
|
||||
delete = __get("ui_kanban_delete", __get("ui_text_delete"))
|
||||
duplicate = __get("ui_kanban_duplicate", __get("ui_graph_duplicate"))
|
||||
create = __get("ui_kanban_create", __create(KEY_A, false, true))
|
||||
rename = __get("ui_kanban_rename", __create(KEY_F2, false, false))
|
||||
search = __get("ui_kanban_search", __get("ui_filedialog_find"))
|
||||
confirm = __get("ui_accept")
|
||||
undo = __get("ui_undo")
|
||||
redo = __get("ui_redo")
|
||||
|
||||
save = __get("ui_kanban_save", __create(KEY_S, false, true))
|
||||
save_as = __get("ui_kanban_save_as", __create(KEY_S, true, true))
|
||||
@@ -0,0 +1 @@
|
||||
uid://c17pmr5kre5dx
|
||||
@@ -0,0 +1,270 @@
|
||||
@tool
|
||||
extends MarginContainer
|
||||
|
||||
## The visual representation of a stage.
|
||||
|
||||
|
||||
const __Singletons := preload("../../plugin_singleton/singletons.gd")
|
||||
const __Shortcuts := preload("../shortcuts.gd")
|
||||
const __EditContext := preload("../edit_context.gd")
|
||||
const __TaskData := preload("../../data/task.gd")
|
||||
const __TaskScene := preload("../task/task.tscn")
|
||||
const __TaskScript := preload("../task/task.gd")
|
||||
const __EditLabel := preload("../../edit_label/edit_label.gd")
|
||||
const __BoardData := preload("../../data/board.gd")
|
||||
const __CategoryPopupMenu := preload("../category/category_popup_menu.gd")
|
||||
|
||||
var board_data: __BoardData
|
||||
var data_uuid: String
|
||||
|
||||
var __category_menu := __CategoryPopupMenu.new()
|
||||
|
||||
@onready var panel_container: PanelContainer = %Panel
|
||||
@onready var title_label: __EditLabel = %Title
|
||||
@onready var create_button: Button = %Create
|
||||
@onready var task_holder: VBoxContainer = %TaskHolder
|
||||
@onready var scroll_container: ScrollContainer = %ScrollContainer
|
||||
@onready var preview: Control = %Preview
|
||||
@onready var preview_color: ColorRect = %Preview/Color
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
update()
|
||||
board_data.get_stage(data_uuid).changed.connect(update.bind(true))
|
||||
|
||||
scroll_container.set_drag_forwarding(
|
||||
_get_drag_data_fw.bind(scroll_container),
|
||||
_can_drop_data_fw.bind(scroll_container),
|
||||
_drop_data_fw.bind(scroll_container),
|
||||
)
|
||||
|
||||
create_button.pressed.connect(__on_create_button_pressed)
|
||||
add_child(__category_menu)
|
||||
__category_menu.uuid_selected.connect(__on_category_create_popup_uuid_selected)
|
||||
__category_menu.popup_hide.connect(create_button.set_pressed_no_signal.bind(false))
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
if ctx.focus == data_uuid:
|
||||
ctx.focus = ""
|
||||
grab_focus()
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseMotion:
|
||||
if not Rect2(Vector2(), size).has_point(get_local_mouse_position()):
|
||||
preview.visible = false
|
||||
|
||||
|
||||
func _shortcut_input(event: InputEvent) -> void:
|
||||
if not __Shortcuts.should_handle_shortcut(self):
|
||||
return
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
if not event.is_echo() and event.is_pressed():
|
||||
if shortcuts.create.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
__category_menu.popup_at_mouse_position(self)
|
||||
|
||||
elif shortcuts.rename.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
title_label.show_edit()
|
||||
|
||||
|
||||
func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
|
||||
preview.visible = true
|
||||
preview.position.y = __target_height_from_position(at_position)
|
||||
|
||||
return data is Dictionary and data.has("task") and data.has("stage")
|
||||
|
||||
|
||||
func _can_drop_data_fw(at_position: Vector2, data: Variant, from: Control) -> bool:
|
||||
var local_pos = (at_position + from.get_global_rect().position) - get_global_rect().position
|
||||
return _can_drop_data(local_pos, data)
|
||||
|
||||
|
||||
func _get_drag_data_fw(at_position: Vector2, from: Control) -> Variant:
|
||||
if from is __TaskScript:
|
||||
var control := Control.new()
|
||||
var rect := ColorRect.new()
|
||||
control.add_child(rect)
|
||||
rect.size = from.get_rect().size
|
||||
rect.position = -at_position
|
||||
rect.color = board_data.get_category(board_data.get_task(from.data_uuid).category).color
|
||||
from.set_drag_preview(control)
|
||||
|
||||
return {
|
||||
"task": from.data_uuid,
|
||||
"stage": data_uuid,
|
||||
}
|
||||
return null
|
||||
|
||||
|
||||
func _drop_data(at_position: Vector2, data: Variant) -> void:
|
||||
var index := __target_index_from_position(at_position)
|
||||
preview.hide()
|
||||
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.undo_redo.create_action("Move task")
|
||||
|
||||
var tasks := board_data.get_stage(data["stage"]).tasks
|
||||
|
||||
if data["stage"] == data_uuid:
|
||||
var old_index := tasks.find(data["task"])
|
||||
if index < old_index:
|
||||
tasks.erase(data["task"])
|
||||
tasks.insert(index, data["task"])
|
||||
elif index > old_index + 1:
|
||||
tasks.erase(data["task"])
|
||||
tasks.insert(index - 1, data["task"])
|
||||
else:
|
||||
tasks.erase(data["task"])
|
||||
|
||||
ctx.undo_redo.add_do_property(board_data.get_stage(data["stage"]), &"tasks", tasks.duplicate())
|
||||
ctx.undo_redo.add_undo_property(board_data.get_stage(data["stage"]), &"tasks", board_data.get_stage(data["stage"]).tasks)
|
||||
|
||||
tasks = board_data.get_stage(data_uuid).tasks
|
||||
tasks.insert(index, data["task"])
|
||||
|
||||
ctx.focus = data["task"]
|
||||
|
||||
ctx.undo_redo.add_do_property(board_data.get_stage(data_uuid), &"tasks", tasks)
|
||||
ctx.undo_redo.add_undo_property(board_data.get_stage(data_uuid), &"tasks", board_data.get_stage(data_uuid).tasks)
|
||||
ctx.undo_redo.commit_action()
|
||||
|
||||
|
||||
func _drop_data_fw(at_position: Vector2, data: Variant, from: Control) -> void:
|
||||
var local_pos = (at_position + from.get_global_rect().position) - get_global_rect().position
|
||||
_drop_data(local_pos, data)
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_THEME_CHANGED and not is_part_of_edited_scene():
|
||||
if is_instance_valid(panel_container):
|
||||
panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Panel"))
|
||||
if is_instance_valid(create_button):
|
||||
create_button.icon = get_theme_icon(&"Add", &"EditorIcons")
|
||||
if is_instance_valid(preview_color):
|
||||
preview_color.color = get_theme_color(&"font_selected_color", &"TabBar")
|
||||
|
||||
|
||||
func update(single: bool = false) -> void:
|
||||
var focus_owner := get_viewport().gui_get_focus_owner()
|
||||
if single:
|
||||
grab_focus()
|
||||
|
||||
if title_label.text_changed.is_connected(__set_title):
|
||||
title_label.text_changed.disconnect(__set_title)
|
||||
title_label.text = board_data.get_stage(data_uuid).title
|
||||
title_label.text_changed.connect(__set_title)
|
||||
|
||||
var old_scroll := scroll_container.scroll_vertical
|
||||
|
||||
if is_instance_valid(focus_owner) and (is_ancestor_of(focus_owner) or focus_owner == self):
|
||||
if focus_owner is __TaskScript:
|
||||
__Singletons.instance_of(__EditContext, self).focus = focus_owner.data_uuid
|
||||
|
||||
for task in task_holder.get_children():
|
||||
task.queue_free()
|
||||
|
||||
for uuid in board_data.get_stage(data_uuid).tasks:
|
||||
var task: __TaskScript = __TaskScene.instantiate()
|
||||
task.board_data = board_data
|
||||
task.data_uuid = uuid
|
||||
task.set_drag_forwarding(
|
||||
_get_drag_data_fw.bind(task),
|
||||
_can_drop_data_fw.bind(task),
|
||||
_drop_data_fw.bind(task),
|
||||
)
|
||||
task_holder.add_child(task)
|
||||
|
||||
custom_minimum_size.x = __Singletons.instance_of(__EditContext, self).settings.stages_width
|
||||
|
||||
scroll_container.scroll_vertical = old_scroll
|
||||
__update_category_menus()
|
||||
|
||||
|
||||
func __update_category_menus() -> void:
|
||||
__category_menu.board_data = board_data
|
||||
|
||||
|
||||
func __target_index_from_position(pos: Vector2) -> int:
|
||||
var global_pos := pos + get_global_position()
|
||||
|
||||
if not scroll_container.get_global_rect().has_point(global_pos):
|
||||
return 0
|
||||
|
||||
var scroll_pos := global_pos - task_holder.get_global_position()
|
||||
var c := 0
|
||||
for task in task_holder.get_children():
|
||||
var y = task.position.y + task.size.y/2
|
||||
if scroll_pos.y < y:
|
||||
return c
|
||||
c += 1
|
||||
|
||||
return task_holder.get_child_count()
|
||||
|
||||
|
||||
func __set_title(value: String) -> void:
|
||||
board_data.get_stage(data_uuid).title = value
|
||||
|
||||
|
||||
func __on_create_button_pressed() -> void:
|
||||
if board_data.get_category_count() > 1:
|
||||
__category_menu.popup_at_local_position(create_button, Vector2(0, create_button.get_global_rect().size.y))
|
||||
else:
|
||||
__create_task(board_data.get_categories()[0])
|
||||
create_button.set_pressed_no_signal(false)
|
||||
|
||||
|
||||
func __create_task(category: String) -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
var stage_data := board_data.get_stage(data_uuid)
|
||||
|
||||
var task_data := __TaskData.new("New task", "", category)
|
||||
var uuid = board_data.add_task(task_data)
|
||||
var tasks = stage_data.tasks
|
||||
tasks.append(uuid)
|
||||
|
||||
ctx.undo_redo.create_action("Add task")
|
||||
ctx.undo_redo.add_do_method(board_data.__add_task.bind(task_data, uuid))
|
||||
ctx.undo_redo.add_do_property(stage_data, &"tasks", tasks)
|
||||
ctx.undo_redo.add_undo_property(stage_data, &"tasks", stage_data.tasks)
|
||||
ctx.undo_redo.add_undo_method(board_data.remove_task.bind(uuid))
|
||||
ctx.undo_redo.commit_action(false)
|
||||
|
||||
stage_data.tasks = tasks
|
||||
|
||||
for task in task_holder.get_children():
|
||||
if task.data_uuid == uuid:
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
task.grab_focus()
|
||||
task.show_edit(__EditLabel.INTENTION.REPLACE)
|
||||
|
||||
ctx.filter = null
|
||||
|
||||
|
||||
func __target_height_from_position(pos: Vector2) -> float:
|
||||
var global_pos = pos + get_global_position()
|
||||
|
||||
if not scroll_container.get_global_rect().has_point(global_pos):
|
||||
return - float(task_holder.get_theme_constant(&"separation")) / 2.0
|
||||
|
||||
var scroll_pos: Vector2 = global_pos - task_holder.get_global_position()
|
||||
var c := 0.0
|
||||
for task in task_holder.get_children():
|
||||
if not task.visible:
|
||||
continue
|
||||
|
||||
var y = task.position.y + task.size.y/2.0
|
||||
if scroll_pos.y < y:
|
||||
return c - float(task_holder.get_theme_constant(&"separation")) / 2.0
|
||||
c += task.size.y + task_holder.get_theme_constant(&"separation")
|
||||
|
||||
return c
|
||||
|
||||
|
||||
func __on_category_create_popup_uuid_selected(uuid) -> void:
|
||||
__create_task(uuid)
|
||||
@@ -0,0 +1 @@
|
||||
uid://efin40vb2jqn
|
||||
@@ -0,0 +1,125 @@
|
||||
[gd_scene format=3 uid="uid://bjmtdjfx7iqgp"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://efin40vb2jqn" path="res://addons/kanban_tasks/view/stage/stage.gd" id="1_i5556"]
|
||||
[ext_resource type="Script" uid="uid://cmnh7scftf1d" path="res://addons/kanban_tasks/edit_label/edit_label.gd" id="2"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h7hiu"]
|
||||
content_margin_left = 4.0
|
||||
content_margin_top = 4.0
|
||||
content_margin_right = 4.0
|
||||
content_margin_bottom = 5.0
|
||||
bg_color = Color(0.1, 0.1, 0.1, 0.6)
|
||||
corner_radius_top_left = 3
|
||||
corner_radius_top_right = 3
|
||||
corner_radius_bottom_right = 3
|
||||
corner_radius_bottom_left = 3
|
||||
corner_detail = 5
|
||||
|
||||
[node name="Stage" type="MarginContainer" unique_id=1130122747]
|
||||
editor_description = "This container is needed because the panel style cannot be updated from a script on the panel container."
|
||||
custom_minimum_size = Vector2(200, 200)
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
focus_mode = 1
|
||||
theme_override_constants/margin_left = 0
|
||||
theme_override_constants/margin_top = 0
|
||||
theme_override_constants/margin_right = 0
|
||||
theme_override_constants/margin_bottom = 0
|
||||
script = ExtResource("1_i5556")
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="." unique_id=1067610553]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_h7hiu")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Panel" unique_id=714031570]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer" unique_id=1792440654]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Title" type="VBoxContainer" parent="Panel/VBoxContainer/HBoxContainer" unique_id=68481716]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 34.1)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
alignment = 1
|
||||
script = ExtResource("2")
|
||||
default_intention = 0
|
||||
|
||||
[node name="Create" type="Button" parent="Panel/VBoxContainer/HBoxContainer" unique_id=1670067720]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Add task."
|
||||
focus_mode = 0
|
||||
toggle_mode = true
|
||||
action_mode = 0
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="Panel/VBoxContainer" unique_id=544264424]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="Panel/VBoxContainer" unique_id=1588977243]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
follow_focus = true
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/VBoxContainer/ScrollContainer" unique_id=1069040072]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 5
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="TaskHolder" type="VBoxContainer" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer" unique_id=482042085]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 5
|
||||
|
||||
[node name="PreviewHolder" type="Control" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer" unique_id=62677879]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="Preview" type="Control" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer/PreviewHolder" unique_id=439308420]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 14
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="Color" type="ColorRect" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer/PreviewHolder/Preview" unique_id=1744652142]
|
||||
custom_minimum_size = Vector2(0, 1)
|
||||
layout_mode = 1
|
||||
anchors_preset = 14
|
||||
anchor_top = 0.5
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 0.5
|
||||
offset_top = -0.5
|
||||
offset_bottom = 0.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
color = Color(0.95, 0.95, 0.95, 1)
|
||||
@@ -0,0 +1,59 @@
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
const __Singletons := preload("../../plugin_singleton/singletons.gd")
|
||||
const __EditContext := preload("../edit_context.gd")
|
||||
|
||||
signal create_board()
|
||||
signal open_board(path: String)
|
||||
|
||||
@onready var create_board_button: LinkButton = %CreateBoard
|
||||
@onready var open_board_button: LinkButton = %OpenBoard
|
||||
@onready var recent_board_holder: VBoxContainer = %RecentBoardHolder
|
||||
@onready var delete_from_recent_dialog: ConfirmationDialog = %DeleteFromRecent
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
create_board_button.pressed.connect(func(): create_board.emit())
|
||||
open_board_button.pressed.connect(func(): open_board.emit(""))
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
ctx.settings.changed.connect(update)
|
||||
update()
|
||||
|
||||
|
||||
func update() -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
|
||||
for child in recent_board_holder.get_children():
|
||||
child.queue_free()
|
||||
|
||||
for board in ctx.settings.recent_files:
|
||||
var button := LinkButton.new()
|
||||
button.underline = LinkButton.UNDERLINE_MODE_NEVER
|
||||
button.text = board
|
||||
button.add_theme_color_override(&"font_color", Color(1, 1, 1, 0.2))
|
||||
button.pressed.connect(__on_open_recent.bind(board))
|
||||
recent_board_holder.add_child(button)
|
||||
|
||||
|
||||
func __delete_from_recent(path: String) -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self) as __EditContext
|
||||
|
||||
var recent = ctx.settings.recent_files
|
||||
var i = recent.find(path)
|
||||
if i >= 0:
|
||||
recent.remove_at(i)
|
||||
ctx.settings.recent_files = recent
|
||||
|
||||
|
||||
func __on_open_recent(path: String) -> void:
|
||||
if not FileAccess.file_exists(path):
|
||||
if delete_from_recent_dialog.confirmed.is_connected(__delete_from_recent):
|
||||
delete_from_recent_dialog.confirmed.disconnect(__delete_from_recent)
|
||||
delete_from_recent_dialog.confirmed.connect(__delete_from_recent.bind(path))
|
||||
delete_from_recent_dialog.popup_centered()
|
||||
return
|
||||
open_board.emit(path)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b671wepab3eng
|
||||
@@ -0,0 +1,103 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://bemcl1rqpeqty"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b671wepab3eng" path="res://addons/kanban_tasks/view/start/start.gd" id="1_fkeby"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_0i4nn"]
|
||||
font_size = 22
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_5febo"]
|
||||
font_size = 20
|
||||
font_color = Color(1, 1, 1, 0.384314)
|
||||
|
||||
[node name="Start" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_fkeby")
|
||||
|
||||
[node name="CenterContainer" type="CenterContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 40
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 11
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = -10
|
||||
|
||||
[node name="Label" type="Label" parent="CenterContainer/HBoxContainer/VBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Kanban Tasks"
|
||||
label_settings = SubResource("LabelSettings_0i4nn")
|
||||
|
||||
[node name="Label2" type="Label" parent="CenterContainer/HBoxContainer/VBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Todo Manager"
|
||||
label_settings = SubResource("LabelSettings_5febo")
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/HBoxContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 0
|
||||
theme_override_constants/margin_right = 0
|
||||
theme_override_constants/margin_bottom = 0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HBoxContainer/VBoxContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="CreateBoard" type="LinkButton" parent="CenterContainer/HBoxContainer/VBoxContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Create Board"
|
||||
underline = 2
|
||||
|
||||
[node name="OpenBoard" type="LinkButton" parent="CenterContainer/HBoxContainer/VBoxContainer/MarginContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Open Board"
|
||||
underline = 2
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="CenterContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="VBoxContainer2" type="VBoxContainer" parent="CenterContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="CenterContainer/HBoxContainer/VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = "Recent Boards"
|
||||
|
||||
[node name="MarginContainer2" type="MarginContainer" parent="CenterContainer/HBoxContainer/VBoxContainer2"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 0
|
||||
theme_override_constants/margin_right = 0
|
||||
theme_override_constants/margin_bottom = 0
|
||||
|
||||
[node name="RecentBoardHolder" type="VBoxContainer" parent="CenterContainer/HBoxContainer/VBoxContainer2/MarginContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="DeleteFromRecent" type="ConfirmationDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Board not found"
|
||||
size = Vector2i(388, 106)
|
||||
ok_button_text = "Delete"
|
||||
dialog_text = "The board does not seem to exist anymore.
|
||||
You may choos to remove it from the recent list."
|
||||
cancel_button_text = "Keep"
|
||||
@@ -0,0 +1,32 @@
|
||||
@tool
|
||||
extends Label
|
||||
|
||||
|
||||
@export var auto_size_height: bool = true:
|
||||
set(value):
|
||||
auto_size_height = value
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
draw.connect(__before_draw)
|
||||
|
||||
|
||||
func __before_draw() -> void:
|
||||
# This is needed if wrapping is turned on in an autosized label,
|
||||
# otherwise the conatiner will give 0 height
|
||||
# (As the label itself cannot decide what size to ask from the container
|
||||
# as due to wrapping, no size is fixed. But fortunatelly the label
|
||||
# makes internal calculation according to intended width before the draw)
|
||||
if auto_size_height:
|
||||
var stylebox := get_theme_stylebox(&"normal")
|
||||
var line_spacing = get_theme_constant(&"line_spacing")
|
||||
var height := max(0, stylebox.content_margin_top)
|
||||
for i in get_line_count():
|
||||
if max_lines_visible >= 0 and i >= max_lines_visible:
|
||||
break
|
||||
if i > 0:
|
||||
height += line_spacing
|
||||
height += get_line_height(i)
|
||||
height += max(0, stylebox.content_margin_bottom)
|
||||
custom_minimum_size.y = height
|
||||
@@ -0,0 +1 @@
|
||||
uid://cqsmighbbfdl5
|
||||
@@ -0,0 +1,441 @@
|
||||
@tool
|
||||
extends MarginContainer
|
||||
|
||||
## The visual representation of a task.
|
||||
|
||||
|
||||
const __Singletons := preload("../../plugin_singleton/singletons.gd")
|
||||
const __Shortcuts := preload("../shortcuts.gd")
|
||||
const __EditContext := preload("../edit_context.gd")
|
||||
const __Filter := preload("../filter.gd")
|
||||
const __BoardData := preload("../../data/board.gd")
|
||||
const __EditLabel := preload("../../edit_label/edit_label.gd")
|
||||
const __ExpandButton := preload("../../expand_button/expand_button.gd")
|
||||
const __TaskData := preload("../../data/task.gd")
|
||||
const __DetailsScript := preload("../details/details.gd")
|
||||
const __StepHolder := preload("../details/step_holder.gd")
|
||||
const __TooltipScript := preload("../tooltip.gd")
|
||||
const __CategoryPopupMenu := preload("../category/category_popup_menu.gd")
|
||||
|
||||
enum ACTIONS {
|
||||
DETAILS,
|
||||
RENAME,
|
||||
DELETE,
|
||||
DUPLICATE,
|
||||
}
|
||||
|
||||
const COLOR_WIDTH: int = 8
|
||||
|
||||
var board_data: __BoardData:
|
||||
set(value):
|
||||
board_data = value
|
||||
__update_category_menu()
|
||||
var data_uuid: String
|
||||
|
||||
var __style_focus: StyleBoxFlat
|
||||
var __style_panel: StyleBoxFlat
|
||||
|
||||
var __category_menu := __CategoryPopupMenu.new()
|
||||
|
||||
@onready var panel_container: PanelContainer = %Panel
|
||||
@onready var category_button: Button = %CategoryButton
|
||||
@onready var title_label: __EditLabel = %Title
|
||||
@onready var description_label: Label = %Description
|
||||
@onready var step_holder: __StepHolder = %StepHolder
|
||||
@onready var expand_button: __ExpandButton = %ExpandButton
|
||||
@onready var edit_button: Button = %Edit
|
||||
@onready var context_menu: PopupMenu = %ContextMenu
|
||||
@onready var details: __DetailsScript = %Details
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
__style_focus = StyleBoxFlat.new()
|
||||
__style_focus.set_border_width_all(1)
|
||||
__style_focus.draw_center = false
|
||||
|
||||
__style_panel = StyleBoxFlat.new()
|
||||
__style_panel.set_border_width_all(0)
|
||||
__style_panel.border_width_left = COLOR_WIDTH
|
||||
__style_panel.draw_center = false
|
||||
if not is_part_of_edited_scene():
|
||||
panel_container.add_theme_stylebox_override(&"panel", __style_panel)
|
||||
|
||||
context_menu.id_pressed.connect(__action)
|
||||
edit_button.pressed.connect(__action.bind(ACTIONS.DETAILS))
|
||||
expand_button.state_changed.connect(func (expanded): __update_step_holder())
|
||||
|
||||
category_button.pressed.connect(__on_category_button_pressed)
|
||||
add_child(__category_menu)
|
||||
__category_menu.uuid_selected.connect(__on_category_menu_uuid_selected)
|
||||
|
||||
notification(NOTIFICATION_THEME_CHANGED)
|
||||
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
|
||||
update()
|
||||
board_data.get_task(data_uuid).changed.connect(update)
|
||||
board_data.changed.connect(__update_category_button)
|
||||
|
||||
if data_uuid == ctx.focus:
|
||||
ctx.focus = ""
|
||||
grab_focus()
|
||||
|
||||
if not ctx.filter_changed.is_connected(__apply_filter):
|
||||
ctx.filter_changed.connect(__apply_filter)
|
||||
ctx.settings.changed.connect(update)
|
||||
__apply_filter()
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT:
|
||||
accept_event()
|
||||
__update_context_menu()
|
||||
context_menu.position = get_global_mouse_position()
|
||||
if not get_window().gui_embed_subwindows:
|
||||
context_menu.position += get_window().position
|
||||
context_menu.popup()
|
||||
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed() and event.is_double_click():
|
||||
__action(ACTIONS.DETAILS)
|
||||
|
||||
|
||||
func _shortcut_input(event: InputEvent) -> void:
|
||||
if not __Shortcuts.should_handle_shortcut(self):
|
||||
return
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
if not event.is_echo() and event.is_pressed():
|
||||
if shortcuts.delete.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
__action(ACTIONS.DELETE)
|
||||
elif shortcuts.confirm.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
__action(ACTIONS.DETAILS)
|
||||
elif shortcuts.rename.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
__action(ACTIONS.RENAME)
|
||||
elif shortcuts.duplicate.matches_event(event):
|
||||
get_viewport().set_input_as_handled()
|
||||
__action(ACTIONS.DUPLICATE)
|
||||
|
||||
|
||||
func _make_custom_tooltip(for_text) -> Object:
|
||||
var tooltip := __TooltipScript.new()
|
||||
tooltip.text = for_text
|
||||
tooltip.mimic_paragraphs()
|
||||
return tooltip
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
match(what):
|
||||
NOTIFICATION_THEME_CHANGED when not is_part_of_edited_scene():
|
||||
if panel_container:
|
||||
var tab_panel = get_theme_stylebox(&"panel", &"TabContainer")
|
||||
if tab_panel is StyleBoxFlat:
|
||||
__style_panel.bg_color = tab_panel.bg_color
|
||||
__style_panel.draw_center = true
|
||||
else:
|
||||
__style_panel.draw_center = false
|
||||
if edit_button:
|
||||
edit_button.icon = get_theme_icon(&"Edit", &"EditorIcons")
|
||||
NOTIFICATION_DRAW:
|
||||
if has_focus():
|
||||
await get_tree().create_timer(0.0).timeout
|
||||
__style_focus.draw(
|
||||
get_canvas_item(),
|
||||
Rect2(
|
||||
panel_container.get_global_rect().position - get_global_rect().position,
|
||||
panel_container.size
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
func update() -> void:
|
||||
if not is_inside_tree():
|
||||
# The node might linger in the undoredo manager.
|
||||
return
|
||||
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
var task := board_data.get_task(data_uuid)
|
||||
var task_category := board_data.get_category(task.category)
|
||||
|
||||
__style_focus.border_color = task_category.color
|
||||
__style_panel.border_color = task_category.color
|
||||
|
||||
category_button.text = task_category.title
|
||||
category_button.visible = ctx.settings.show_category_on_board
|
||||
|
||||
if ctx.settings.show_description_preview:
|
||||
var description: String
|
||||
match ctx.settings.description_on_board:
|
||||
ctx.settings.DescriptionOnBoard.FIRST_LINE:
|
||||
description = task.description
|
||||
var idx := description.find("\n")
|
||||
description = description.substr(0, idx)
|
||||
ctx.settings.DescriptionOnBoard.UNTIL_FIRST_BLANK_LINE:
|
||||
description = task.description
|
||||
var idx := description.find("\n\n")
|
||||
description = description.substr(0, idx)
|
||||
_:
|
||||
description = task.description
|
||||
description_label.text = description
|
||||
if ctx.settings.max_displayed_lines_in_description > 0:
|
||||
description_label.max_lines_visible = ctx.settings.max_displayed_lines_in_description
|
||||
else:
|
||||
description_label.max_lines_visible = -1
|
||||
description_label.visible = ctx.settings.show_description_preview and description_label.text.strip_edges().length() != 0
|
||||
else:
|
||||
description_label.text = ""
|
||||
description_label.visible = (description_label.text.length() > 0)
|
||||
|
||||
__update_step_holder()
|
||||
|
||||
var steps := board_data.get_task(data_uuid).steps
|
||||
for step in steps:
|
||||
if not step.changed.is_connected(__update_step_holder):
|
||||
step.changed.connect(__update_step_holder)
|
||||
|
||||
if title_label.text_changed.is_connected(__set_title):
|
||||
title_label.text_changed.disconnect(__set_title)
|
||||
title_label.text = board_data.get_task(data_uuid).title
|
||||
title_label.text_changed.connect(__set_title)
|
||||
|
||||
__update_category_menu()
|
||||
__update_category_button()
|
||||
__update_tooltip()
|
||||
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func show_edit(intention: __EditLabel.INTENTION) -> void:
|
||||
title_label.show_edit(intention)
|
||||
|
||||
|
||||
func __update_step_holder() -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
var task := board_data.get_task(data_uuid)
|
||||
var expanded := expand_button.expanded
|
||||
|
||||
step_holder.clear_steps()
|
||||
var step_count := 0
|
||||
var expandable := false
|
||||
|
||||
if ctx.settings.show_steps_preview:
|
||||
var steps := board_data.get_task(data_uuid).steps
|
||||
var max_step_count := ctx.settings.max_steps_on_board
|
||||
match ctx.settings.steps_on_board:
|
||||
ctx.settings.StepsOnBoard.ONLY_OPEN:
|
||||
for i in steps.size():
|
||||
if steps[i].done:
|
||||
continue
|
||||
if max_step_count > 0 and step_count >= max_step_count:
|
||||
expandable = true
|
||||
if not expanded:
|
||||
break
|
||||
step_holder.add_step(steps[i])
|
||||
step_count += 1
|
||||
ctx.settings.StepsOnBoard.ALL_OPEN_FIRST:
|
||||
for i in steps.size():
|
||||
if steps[i].done:
|
||||
continue
|
||||
if max_step_count > 0 and step_count >= max_step_count:
|
||||
expandable = true
|
||||
if not expanded:
|
||||
break
|
||||
step_holder.add_step(steps[i])
|
||||
step_count += 1
|
||||
for i in steps.size():
|
||||
if not steps[i].done:
|
||||
continue
|
||||
if max_step_count > 0 and step_count >= max_step_count:
|
||||
expandable = true
|
||||
if not expanded:
|
||||
break
|
||||
step_holder.add_step(steps[i])
|
||||
step_count += 1
|
||||
ctx.settings.StepsOnBoard.ALL_IN_ORDER:
|
||||
for i in steps.size():
|
||||
if max_step_count > 0 and step_count >= max_step_count:
|
||||
expandable = true
|
||||
if not expanded:
|
||||
break
|
||||
step_holder.add_step(steps[i])
|
||||
step_count += 1
|
||||
_:
|
||||
pass
|
||||
step_holder.visible = (step_count > 0)
|
||||
expand_button.visible = expandable
|
||||
|
||||
|
||||
func __update_category_menu() -> void:
|
||||
__category_menu.board_data = board_data
|
||||
|
||||
|
||||
func __update_category_button() -> void:
|
||||
if board_data.get_category_count() > 1:
|
||||
category_button.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
else:
|
||||
category_button.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
|
||||
|
||||
func __update_tooltip() -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
var task := board_data.get_task(data_uuid)
|
||||
var task_category := board_data.get_category(task.category)
|
||||
var steps := board_data.get_task(data_uuid).steps
|
||||
|
||||
var category_bullet = "[bgcolor=#" + task_category.color.to_html(false) + "] [/bgcolor] "
|
||||
#var category_bullet = "[color=#" + task_category.color.to_html(false) + "]\u2588\u2588[/color]"
|
||||
#var category_bullet = "[color=#" + task_category.color.to_html(false) + "]\u220E[/color]"
|
||||
#var category_bullet = "[color=#" + task_category.color.to_html(false) + "]\u25A0[/color]"
|
||||
tooltip_text = category_bullet + " " + board_data.get_category(task.category).title + ": " + task.title
|
||||
if task.description !=null and task.description.length() > 0:
|
||||
tooltip_text += "[p]" + task.description + "[/p]"
|
||||
|
||||
var open_steps = []
|
||||
var done_steps = []
|
||||
for step in steps:
|
||||
(done_steps if step.done else open_steps).append(step)
|
||||
#var open_step_bullet = "\u25A1" # Unfilled square
|
||||
#var open_step_bullet = "[color=#808080]\u25A0[/color]" # Filled gray square
|
||||
#var done_step_bullet = "\u25A0" # Filled square
|
||||
#var open_step_bullet = "\u2718" # Heavy ballot X
|
||||
#var done_step_bullet = "\u2714" # Heavy check mark
|
||||
#var open_step_bullet = "\u2717" # Ballot X
|
||||
#var done_step_bullet = "\u2713" # Check mark
|
||||
var open_step_bullet = "[color=#F08080]\u25A0[/color]" # Filled red square
|
||||
var done_step_bullet = "[color=#98FB98]\u25A0[/color]" # Filled green square
|
||||
if open_steps.size() > 0 or done_steps.size() > 0:
|
||||
tooltip_text += "[p]"
|
||||
if open_steps.size() > 0:
|
||||
tooltip_text += "Open steps:\n[table=2]"
|
||||
for step in open_steps:
|
||||
tooltip_text += "[cell]" + open_step_bullet + "[/cell][cell]" + step.details + "[/cell]\n"
|
||||
tooltip_text += "[/table]\n"
|
||||
if done_steps.size() > 0:
|
||||
tooltip_text += "Done steps:\n[table=2]"
|
||||
for step in done_steps:
|
||||
tooltip_text += "[cell]" + done_step_bullet + "[/cell][cell]" + step.details + "[/cell]\n"
|
||||
tooltip_text += "[/table]\n"
|
||||
tooltip_text += "[/p]"
|
||||
|
||||
|
||||
func __apply_filter() -> void:
|
||||
var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
|
||||
|
||||
if not ctx.filter or ctx.filter.text.length() == 0:
|
||||
show()
|
||||
return
|
||||
|
||||
var task = board_data.get_task(data_uuid)
|
||||
var filter_simple := __simplify_string(ctx.filter.text)
|
||||
var filter_matches := false
|
||||
if not filter_matches:
|
||||
var text_simple := __simplify_string(task.title)
|
||||
if text_simple.matchn("*" + filter_simple + "*"):
|
||||
filter_matches = true
|
||||
if not filter_matches:
|
||||
var category = board_data.get_category(task.category)
|
||||
var text_simple := __simplify_string(category.title)
|
||||
if text_simple.matchn("*" + filter_simple + "*"):
|
||||
filter_matches = true
|
||||
if not filter_matches and ctx.filter.advanced:
|
||||
var text_simple := __simplify_string(task.description)
|
||||
if text_simple.matchn("*" + filter_simple + "*"):
|
||||
filter_matches = true
|
||||
if not filter_matches and ctx.filter.advanced:
|
||||
for step in task.steps:
|
||||
if not filter_matches:
|
||||
var text_simple := __simplify_string(step.details)
|
||||
if text_simple.matchn("*" + filter_simple + "*"):
|
||||
filter_matches = true
|
||||
else:
|
||||
break
|
||||
|
||||
if filter_matches:
|
||||
show()
|
||||
else:
|
||||
hide()
|
||||
|
||||
|
||||
func __simplify_string(string: String) -> String:
|
||||
return string.replace(" ", "").replace("\t", "")
|
||||
|
||||
|
||||
func __update_context_menu() -> void:
|
||||
var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
|
||||
|
||||
context_menu.clear()
|
||||
context_menu.add_item("Details", ACTIONS.DETAILS)
|
||||
|
||||
context_menu.add_separator()
|
||||
|
||||
context_menu.add_icon_item(get_theme_icon(&"Rename", &"EditorIcons"), "Rename", ACTIONS.RENAME)
|
||||
context_menu.set_item_shortcut(context_menu.get_item_index(ACTIONS.RENAME), shortcuts.rename)
|
||||
|
||||
context_menu.add_icon_item(get_theme_icon(&"Duplicate", &"EditorIcons"), "Duplicate", ACTIONS.DUPLICATE)
|
||||
context_menu.set_item_shortcut(context_menu.get_item_index(ACTIONS.DUPLICATE), shortcuts.duplicate)
|
||||
|
||||
context_menu.add_icon_item(get_theme_icon(&"Remove", &"EditorIcons"), "Delete", ACTIONS.DELETE)
|
||||
context_menu.set_item_shortcut(context_menu.get_item_index(ACTIONS.DELETE), shortcuts.delete)
|
||||
|
||||
|
||||
func __action(action) -> void:
|
||||
var undo_redo: UndoRedo = __Singletons.instance_of(__EditContext, self).undo_redo
|
||||
|
||||
match(action):
|
||||
ACTIONS.DELETE:
|
||||
var task = board_data.get_task(data_uuid)
|
||||
for uuid in board_data.get_stages():
|
||||
var tasks := board_data.get_stage(uuid).tasks
|
||||
if data_uuid in tasks:
|
||||
tasks.erase(data_uuid)
|
||||
undo_redo.create_action("Delete task")
|
||||
undo_redo.add_do_property(board_data.get_stage(uuid), &"tasks", tasks)
|
||||
undo_redo.add_do_method(board_data.remove_task.bind(data_uuid, true))
|
||||
undo_redo.add_undo_method(board_data.__add_task.bind(task, data_uuid))
|
||||
undo_redo.add_undo_property(board_data.get_stage(uuid), &"tasks", board_data.get_stage(uuid).tasks)
|
||||
undo_redo.add_undo_reference(task)
|
||||
undo_redo.commit_action()
|
||||
break
|
||||
|
||||
ACTIONS.DETAILS:
|
||||
details.board_data = board_data
|
||||
details.data_uuid = data_uuid
|
||||
details.popup_centered_ratio_no_fullscreen(0.5)
|
||||
|
||||
ACTIONS.DUPLICATE:
|
||||
var copy := __TaskData.new()
|
||||
copy.from_json(board_data.get_task(data_uuid).to_json())
|
||||
var copy_uuid := board_data.add_task(copy)
|
||||
for uuid in board_data.get_stages():
|
||||
var tasks := board_data.get_stage(uuid).tasks
|
||||
if data_uuid in tasks:
|
||||
tasks.insert(tasks.find(data_uuid), copy_uuid)
|
||||
undo_redo.create_action("Duplicate task")
|
||||
undo_redo.add_do_method(board_data.__add_task.bind(copy, copy_uuid))
|
||||
undo_redo.add_do_property(board_data.get_stage(uuid), &"tasks", tasks)
|
||||
undo_redo.add_undo_property(board_data.get_stage(uuid), &"tasks", board_data.get_stage(uuid).tasks)
|
||||
undo_redo.add_undo_method(board_data.remove_task.bind(copy_uuid))
|
||||
undo_redo.commit_action(false)
|
||||
|
||||
board_data.get_stage(uuid).tasks = tasks
|
||||
break
|
||||
|
||||
ACTIONS.RENAME:
|
||||
if context_menu.visible:
|
||||
await context_menu.popup_hide
|
||||
title_label.show_edit()
|
||||
|
||||
|
||||
func __set_title(value: String) -> void:
|
||||
board_data.get_task(data_uuid).title = value
|
||||
|
||||
|
||||
func __on_category_button_pressed() -> void:
|
||||
__category_menu.popup_at_local_position(category_button, Vector2(0, category_button.size.y))
|
||||
|
||||
|
||||
func __on_category_menu_uuid_selected(category_uuid) -> void:
|
||||
var task = board_data.get_task(data_uuid)
|
||||
task.category = category_uuid
|
||||
@@ -0,0 +1 @@
|
||||
uid://bmsawo6qqtluu
|
||||
@@ -0,0 +1,103 @@
|
||||
[gd_scene format=3 uid="uid://ckqrwj5kxr6vl"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bmsawo6qqtluu" path="res://addons/kanban_tasks/view/task/task.gd" id="1_dslv8"]
|
||||
[ext_resource type="Script" uid="uid://cmnh7scftf1d" path="res://addons/kanban_tasks/edit_label/edit_label.gd" id="2_iitpi"]
|
||||
[ext_resource type="Script" uid="uid://cqsmighbbfdl5" path="res://addons/kanban_tasks/view/task/autosize_label.gd" id="3_1qkab"]
|
||||
[ext_resource type="PackedScene" uid="uid://bwi22eyrmeeet" path="res://addons/kanban_tasks/view/details/details.tscn" id="3_2ol5j"]
|
||||
[ext_resource type="PackedScene" uid="uid://dwjg5vyxx4g48" path="res://addons/kanban_tasks/view/details/step_holder.tscn" id="4_4e7a7"]
|
||||
[ext_resource type="Script" uid="uid://guc56bqr2khq" path="res://addons/kanban_tasks/expand_button/expand_button.gd" id="5_sgwao"]
|
||||
|
||||
[node name="Task" type="MarginContainer" unique_id=698399779]
|
||||
editor_description = "This container is needed because the panel style cannot be updated from a script on the panel container."
|
||||
custom_minimum_size = Vector2(150, 0)
|
||||
offset_right = 150.0
|
||||
offset_bottom = 50.0
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 2
|
||||
script = ExtResource("1_dslv8")
|
||||
|
||||
[node name="Panel" type="PanelContainer" parent="." unique_id=1704620960]
|
||||
unique_name_in_owner = true
|
||||
show_behind_parent = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel" unique_id=878429944]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="Panel/HBoxContainer" unique_id=2075946638]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 5
|
||||
theme_override_constants/margin_top = 5
|
||||
theme_override_constants/margin_right = 0
|
||||
theme_override_constants/margin_bottom = 5
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Panel/HBoxContainer/MarginContainer" unique_id=1102637383]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer" unique_id=1244035275]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="CategoryButton" type="Button" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1009888406]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
|
||||
[node name="Title" type="VBoxContainer" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer/HBoxContainer" unique_id=562777166]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 34.1)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
alignment = 1
|
||||
script = ExtResource("2_iitpi")
|
||||
|
||||
[node name="Description" type="Label" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer" unique_id=1197087444]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
modulate = Color(1, 1, 1, 0.443137)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
autowrap_mode = 3
|
||||
text_overrun_behavior = 3
|
||||
script = ExtResource("3_1qkab")
|
||||
|
||||
[node name="StepHolder" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer" unique_id=1751671221 instance=ExtResource("4_4e7a7")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
scrollable = false
|
||||
steps_can_be_removed = false
|
||||
steps_can_be_reordered = false
|
||||
steps_have_context_menu = false
|
||||
|
||||
[node name="ExpandButton" type="Button" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer" unique_id=1264164075]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
theme_type_variation = &"ExpandButton"
|
||||
flat = true
|
||||
icon_alignment = 1
|
||||
script = ExtResource("5_sgwao")
|
||||
expanded = false
|
||||
|
||||
[node name="Edit" type="Button" parent="Panel/HBoxContainer" unique_id=1289048325]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
flat = true
|
||||
|
||||
[node name="ContextMenu" type="PopupMenu" parent="." unique_id=1856471290]
|
||||
unique_name_in_owner = true
|
||||
oversampling_override = 1.0
|
||||
allow_search = false
|
||||
|
||||
[node name="Details" parent="." unique_id=1680695291 instance=ExtResource("3_2ol5j")]
|
||||
unique_name_in_owner = true
|
||||
@@ -0,0 +1,47 @@
|
||||
@tool
|
||||
extends RichTextLabel
|
||||
|
||||
|
||||
@export var mimicked_paragraph_spacing_font_size: int = 6
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
bbcode_enabled = true
|
||||
fit_content = true
|
||||
custom_minimum_size.x = 500
|
||||
resized.connect(__on_resized)
|
||||
|
||||
|
||||
func _notification(what) -> void:
|
||||
match what:
|
||||
NOTIFICATION_ENTER_TREE:
|
||||
__take_over_label_style()
|
||||
|
||||
|
||||
func mimic_paragraphs() -> void:
|
||||
var what_in_order: PackedStringArray = [
|
||||
"[/p]\n[p]",
|
||||
"[/p][p]",
|
||||
"[p][/p]",
|
||||
"[p]",
|
||||
"[/p]",
|
||||
]
|
||||
var forwhat = "\n[font_size=%s]\n[/font_size]\n" % mimicked_paragraph_spacing_font_size
|
||||
var new_text := text
|
||||
new_text = new_text.trim_prefix("[p]").trim_suffix("[/p]")
|
||||
for what in what_in_order:
|
||||
new_text = new_text.replace(what, forwhat)
|
||||
new_text = new_text.trim_prefix("\n").trim_suffix("\n")
|
||||
text = new_text
|
||||
|
||||
|
||||
func __take_over_label_style() -> void:
|
||||
add_theme_stylebox_override(&"normal", get_theme_stylebox(&"normal", &"Label"))
|
||||
|
||||
|
||||
func __on_resized() -> void:
|
||||
# Reduce width if unnecessary, as there is no line wraps
|
||||
var stylebox = get_theme_stylebox(&"normal")
|
||||
var required_width = get_content_width() + stylebox.content_margin_left + stylebox.content_margin_right
|
||||
if required_width < custom_minimum_size.x:
|
||||
custom_minimum_size.x = required_width
|
||||
@@ -0,0 +1 @@
|
||||
uid://c4xhesd3gxby6
|
||||
Reference in New Issue
Block a user