OverviewFilesCommitsIssuesDocsReleasesPeers

Attila772-hypergodot commits / 5520ec7

deleted addon
5520ec78c37f5bc3bab8a2be4fb6daa2211d5844
by Attila772 <71554727+Attila772@users.noreply.github.com> · 2025-05-23T09:28:58.000Z

Changed files (89) +1 -5577

addons/kanban_tasks/LICENSE +0 -19
……@@ -1,19 +0,0 @@
1Copyright (c) 2022-2024 HolonProduction
2
3Permission is hereby granted, free of charge, to any person obtaining a copy
4of this software and associated documentation files (the "Software"), to deal
5in the Software without restriction, including without limitation the rights
6to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7copies of the Software, and to permit persons to whom the Software is
8furnished to do so, subject to the following conditions:
9
10The above copyright notice and this permission notice shall be included in all
11copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19SOFTWARE.
addons/kanban_tasks/data/board.gd +0 -305
……@@ -1,305 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Manages the loading and saving of other data.
5
6
7const __UUID := preload("../uuid/uuid.gd")
8const __Category := preload("category.gd")
9const __Layout := preload("layout.gd")
10const __Stage := preload("stage.gd")
11const __Task := preload("task.gd")
12const __KanbanResource := preload("kanban_resource.gd")
13
14var layout: __Layout:
15 set(value):
16 if layout:
17 layout.changed.disconnect(__notify_changed)
18 layout = value
19 layout.changed.connect(__notify_changed)
20
21var __categories: Dictionary
22var __stages: Dictionary
23var __tasks: Dictionary
24
25
26## Generates a json representation of the board.
27func to_json() -> Dictionary:
28 var dict := {}
29
30 var category_data := __propagate_uuid_dict(__categories)
31 dict["categories"] = category_data
32
33 var stage_data := __propagate_uuid_dict(__stages)
34 dict["stages"] = stage_data
35
36 var task_data := __propagate_uuid_dict(__tasks)
37 dict["tasks"] = task_data
38
39 dict["layout"] = layout.to_json()
40
41 return dict
42
43
44## Save the board at `path`.
45func save(path: String) -> void:
46 var file = FileAccess.open(path, FileAccess.WRITE)
47 if not file:
48 push_error("Error " + str(FileAccess.get_open_error()) + " while opening file for saving board data at " + path)
49 file.close()
50 return
51
52 var string := JSON.stringify(to_json(), "\t", false)
53 file.store_string(string)
54 file.close()
55
56
57## Initializes the board state from json data.
58func from_json(json: Dictionary) -> void:
59 __instantiate_uuid_array(json.get("categories", null), __Category, __add_category)
60 __instantiate_uuid_array(json.get("stages", null), __Stage, __add_stage)
61 __instantiate_uuid_array(json.get("tasks", null), __Task, __add_task)
62
63 layout = __Layout.new([])
64 if json.get("layout", null) is Dictionary:
65 layout.from_json(json["layout"])
66 else:
67 push_warning("Loading incomplete board data which is missing layout data.")
68
69
70## Loads the data from `path` into the current instance.
71func load(path: String) -> void:
72 var file = FileAccess.open(path, FileAccess.READ)
73 if not file:
74 push_error("Error " + str(FileAccess.get_open_error()) + " while opening file for loading board data at " + path)
75 file.close()
76 return
77
78 var json = JSON.new()
79 var err = json.parse(file.get_as_text())
80 file.close()
81 if err != OK:
82 push_error("Error " + str(err) + " while parsing board at " + path + " to json. At line " + str(json.get_error_line()) + " the following problem occured:\n" + json.get_error_message())
83 return
84
85 if json.data.has("columns"):
86 __from_legacy_file(json.data)
87 else:
88 from_json(json.data)
89
90
91## Adds a category and returns the uuid which is associated with it.
92func add_category(category: __Category, silent: bool = false) -> String:
93 var res := __add_category(category)
94 if not silent:
95 __notify_changed()
96 return res
97
98## Returns the category associated with the given uuid or `null` if there is none.
99func get_category(uuid: String) -> __Category:
100 if not __categories.has(uuid) and uuid != "":
101 push_warning('There is no category with the uuid "' + uuid + '".')
102 return __categories.get(uuid, null)
103
104## Returns the count of categories.
105func get_category_count() -> int:
106 return len(__categories)
107
108## Returns the uuid's of all categories.
109func get_categories() -> Array[String]:
110 var temp: Array[String] = []
111 temp.assign(__categories.keys())
112 return temp
113
114## Removes a category by uuid.
115func remove_category(uuid: String, silent: bool = false) -> void:
116 if __categories.has(uuid):
117 __categories[uuid].changed.disconnect(__notify_changed)
118 __categories.erase(uuid)
119 if not silent:
120 __notify_changed()
121 else:
122 push_warning("Trying to remove uuid wich is not associated with a category.")
123
124
125## Adds a stage and returns the uuid which is associated with it.
126func add_stage(stage: __Stage, silent: bool = false) -> String:
127 var res := __add_stage(stage)
128 if not silent:
129 __notify_changed()
130 return res
131
132## Returns the stage associated with the given uuid or `null` if there is none.
133func get_stage(uuid: String) -> __Stage:
134 if not __stages.has(uuid) and uuid != "":
135 push_warning('There is no stage with the uuid "' + uuid + '".')
136 return __stages.get(uuid, null)
137
138## Returns the count of stages.
139func get_stage_count() -> int:
140 return len(__stages)
141
142## Returns the uuid's of all stages.
143func get_stages() -> Array[String]:
144 var temp: Array[String] = []
145 temp.assign(__stages.keys())
146 return temp
147
148## Removes a stage by uuid.
149func remove_stage(uuid: String, silent: bool = false) -> void:
150 if __stages.has(uuid):
151 __stages[uuid].changed.disconnect(__notify_changed)
152 __stages.erase(uuid)
153 if not silent:
154 __notify_changed()
155 else:
156 push_warning("Trying to remove uuid wich is not associated with a stage.")
157
158
159## Adds a task and returns the uuid which is associated with it.
160func add_task(task: __Task, silent: bool = false) -> String:
161 var res := __add_task(task)
162 if not silent:
163 __notify_changed()
164 return res
165
166## Returns the task associated with the given uuid or `null` if there is none.
167func get_task(uuid: String) -> __Task:
168 if not __tasks.has(uuid) and uuid != "":
169 push_warning('There is no task with the uuid "' + uuid + '".')
170 return __tasks.get(uuid, null)
171
172## Returns the count of tasks.
173func get_task_count() -> int:
174 return len(__tasks)
175
176## Returns the uuid's of all tasks.
177func get_tasks() -> Array[String]:
178 var temp: Array[String] = []
179 temp.assign(__tasks.keys())
180 return temp
181
182## Removes a task by uuid.
183func remove_task(uuid: String, silent: bool = false) -> void:
184 if __tasks.has(uuid):
185 if __tasks[uuid].changed.is_connected(__notify_changed):
186 __tasks[uuid].changed.disconnect(__notify_changed)
187 __tasks.erase(uuid)
188 if not silent:
189 __notify_changed()
190 else:
191 push_warning("Trying to remove uuid wich is not associated with a task.")
192
193
194# Internal version of `add_category` which can be provided with an uuid suggestion.
195# The uuid that is passed can be altered by the board if it is already used by
196# an other category. Therefore always use the returned uuid.
197func __add_category(category: __Category, uuid: String = "") -> String:
198 category.changed.connect(__notify_changed)
199
200 if __categories.has(uuid):
201 push_warning("The uuid " + uuid + ' is already used. A new one will be generated for the category "' + category.title + '".')
202
203 if uuid == "":
204 uuid = __UUID.v4()
205
206 while uuid in __categories.keys():
207 uuid = __UUID.v4()
208
209 __categories[uuid] = category
210 return uuid
211
212
213# Internal version of `add_stage` which can be provided with an uuid suggestion.
214func __add_stage(stage: __Stage, uuid: String = "") -> String:
215 stage.changed.connect(__notify_changed)
216
217 if __stages.has(uuid):
218 push_warning("The uuid " + uuid + ' is already used. A new one will be generated for the stage "' + stage.title + '".')
219
220 if uuid == "":
221 uuid = __UUID.v4()
222
223 while uuid in __stages.keys():
224 uuid = __UUID.v4()
225
226 __stages[uuid] = stage
227 return uuid
228
229
230# Internal version of `add_task` which can be provided with an uuid suggestion.
231func __add_task(task: __Task, uuid: String = "") -> String:
232 task.changed.connect(__notify_changed)
233
234 if __tasks.has(uuid):
235 push_warning("The uuid " + uuid + ' is already used. A new one will be generated for the task "' + task.title + '".')
236
237 if uuid == "":
238 uuid = __UUID.v4()
239
240 while uuid in __tasks.keys():
241 uuid = __UUID.v4()
242
243 __tasks[uuid] = task
244 return uuid
245
246
247# HACK: `array` should have the type `Array` but then `null` could not be passed.
248func __instantiate_uuid_array(array, type: Script, add_callback: Callable) -> void:
249 if array == null:
250 push_warning("Loading incomplete board data which is missing data for '" + type.resource_path + "'.")
251 return
252
253 for data in array:
254 var instance: __KanbanResource = type.new()
255 instance.from_json(data)
256 add_callback.call(instance, data.get("uuid", ""))
257
258
259# Converts a dictionary with (uuid, kanban_resource) pairs into a list
260# json representations with the uuid added.
261func __propagate_uuid_dict(dict: Dictionary) -> Array:
262 var res := []
263 for key in dict.keys():
264 var json: Dictionary = {"uuid": key}
265 json.merge(dict[key].to_json())
266 res.append(json)
267 return res
268
269
270# TODO: Remove this sometime in the future.
271## Loads a board from the old file format.
272func __from_legacy_file(data: Dictionary) -> void:
273 var categories: Array[String] = []
274 var tasks: Array[String] = []
275 var stages: Array[String] = []
276
277 for c in data["categories"]:
278 categories.append(
279 __add_category(__Category.new(c["title"], c["color"])),
280 )
281
282 for t in data["tasks"]:
283 tasks.append(
284 __add_task(
285 __Task.new(t["title"], t["details"], categories[t["category"]]),
286 ),
287 )
288
289 for s in data["stages"]:
290 var contained_tasks: Array[String] = []
291 for t in s["tasks"]:
292 contained_tasks.append(tasks[t])
293 stages.append(
294 __add_stage(
295 __Stage.new(s["title"], contained_tasks),
296 ),
297 )
298
299 var columns: Array[PackedStringArray] = []
300 for c in data["columns"]:
301 var column = PackedStringArray([])
302 for s in c["stages"]:
303 column.append(stages[s])
304 columns.append(column)
305 layout = __Layout.new(columns)
addons/kanban_tasks/data/board.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://der5cy3nggnqm
addons/kanban_tasks/data/category.gd +0 -43
……@@ -1,43 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Data of a category.
5
6
7var title: String:
8 set(value):
9 title = value
10 __notify_changed()
11
12var color: Color:
13 set(value):
14 color = value
15 __notify_changed()
16
17
18func _init(p_title: String = "", p_color: Color = Color()) -> void:
19 title = p_title
20 color = p_color
21 super._init()
22
23
24func to_json() -> Dictionary:
25 return {
26 "title": title,
27 "color": color.to_html(false),
28 }
29
30
31func from_json(json: Dictionary) -> void:
32 title = "Missing data."
33 color = Color.CORNFLOWER_BLUE
34
35 if json.has("title"):
36 title = json["title"]
37 else:
38 push_warning("Loading incomplete json data which is missing a title.")
39
40 if json.has("color"):
41 color = Color.html(json["color"])
42 else:
43 push_warning("Loading incomplete json data which is missing a color.")
addons/kanban_tasks/data/category.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://d27427a5moiqa
addons/kanban_tasks/data/kanban_resource.gd +0 -30
……@@ -1,30 +0,0 @@
1@tool
2extends RefCounted
3
4## Base class for kanban tasks data structures.
5
6
7## Emitted when the resource changed. The properties are updated before emitting.
8signal changed()
9
10var __emit_changed := true
11
12
13func _init() -> void:
14 pass
15
16
17## Serializes the object as json.
18func to_json() -> Dictionary:
19 push_error("Method to_json not implemented.")
20 return {}
21
22
23## Deserializes the object from json.
24func from_json(json: Dictionary) -> void:
25 push_error("Method from_json not implemented.")
26
27
28func __notify_changed() -> void:
29 if __emit_changed:
30 changed.emit()
addons/kanban_tasks/data/kanban_resource.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://bhulepo2rpgy6
addons/kanban_tasks/data/layout.gd +0 -49
……@@ -1,49 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Layout data.
5
6
7# Use `PackedStringArray` because nested typed collections are not supported.
8var columns: Array[PackedStringArray] = []:
9 get:
10 return columns.duplicate()
11 set(value):
12 columns = value
13 __notify_changed()
14
15
16func _init(p_columns: Array[PackedStringArray] = []) -> void:
17 columns = p_columns
18 super._init()
19
20
21func to_json() -> Dictionary:
22 var cols := []
23 for c in columns:
24 var col = []
25 for uuid in c:
26 col.append(uuid)
27 cols.append(col)
28 return {
29 "columns": cols,
30 }
31
32
33func from_json(json: Dictionary) -> void:
34 if json.has("columns"):
35 if json["columns"] is Array:
36 var cols: Array[PackedStringArray] = []
37 for c in json["columns"]:
38 var arr := PackedStringArray()
39 if c is Array:
40 for id in c:
41 arr.append(id)
42 else:
43 push_warning("Layout data is corrupted.")
44 cols.append(arr)
45 columns = cols
46 else:
47 push_warning("Layout data is corrupted.")
48 else:
49 push_warning("Loading incomplete json data which is missing a list of columns.")
addons/kanban_tasks/data/layout.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://6e7xmg2w886m
addons/kanban_tasks/data/settings.gd +0 -151
……@@ -1,151 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Contains settings that are not bound to a board.
5
6
7const DEFAULT_EDITOR_DATA_PATH: String = "res://kanban_tasks_data.kanban"
8
9enum DescriptionOnBoard {
10 FULL,
11 FIRST_LINE,
12 UNTIL_FIRST_BLANK_LINE,
13}
14
15enum StepsOnBoard {
16 ONLY_OPEN,
17 ALL_OPEN_FIRST,
18 ALL_IN_ORDER
19}
20
21## Whether the first line of the description is shown on the board.
22var show_description_preview: bool = true:
23 set(value):
24 show_description_preview = value
25 __notify_changed()
26
27var show_steps_preview: bool = true:
28 set(value):
29 show_steps_preview = value
30 __notify_changed()
31
32var show_category_on_board: bool = false:
33 set(value):
34 show_category_on_board = value
35 __notify_changed()
36
37var edit_step_details_exclusively: bool = false:
38 set(value):
39 edit_step_details_exclusively = value
40 __notify_changed()
41
42var max_displayed_lines_in_description: int = 0:
43 set(value):
44 max_displayed_lines_in_description = value
45 __notify_changed()
46
47var description_on_board := DescriptionOnBoard.FIRST_LINE:
48 set(value):
49 description_on_board = value
50 __notify_changed()
51
52var steps_on_board := StepsOnBoard.ONLY_OPEN:
53 set(value):
54 steps_on_board = value
55 __notify_changed()
56
57var max_steps_on_board: int = 2:
58 set(value):
59 max_steps_on_board = value
60 __notify_changed()
61
62var editor_data_file_path: String = DEFAULT_EDITOR_DATA_PATH:
63 set(value):
64 editor_data_file_path = value
65 __notify_changed()
66
67var warn_about_empty_deletion: bool = false:
68 set(value):
69 warn_about_empty_deletion = value
70 __notify_changed()
71
72var recent_file_count: int = 5:
73 set(value):
74 recent_file_count = value
75 recent_files.resize(value)
76 __notify_changed()
77
78var recent_files: PackedStringArray = []:
79 get:
80 return recent_files.duplicate()
81 set(value):
82 recent_files = value
83 __notify_changed()
84
85# Here such settings can come, which is own responsibiity of a user control.
86# When it just want to persist its own state, but the setting is not used by anything else.
87# In this case there is no need to mess up this class with bolerplate code
88# E.g. the splitter position in the details editor window
89# Set via set_internal_state to trigger notification
90# (As no clean-up, during develolpment some mess can remain in it.
91# Use clear or erase in your code in such cases, just don't forget there)
92var internal_states: Dictionary = { }
93
94
95func set_internal_state(property: String, value: Variant) -> void:
96 internal_states[property] = value
97 __notify_changed()
98
99
100func to_json() -> Dictionary:
101 var res := {
102 "show_description_preview": show_description_preview,
103 "warn_about_empty_deletion": warn_about_empty_deletion,
104 "edit_step_details_exclusively": edit_step_details_exclusively,
105 "max_displayed_lines_in_description": max_displayed_lines_in_description,
106 "description_on_board": description_on_board,
107 "show_steps_preview": show_steps_preview,
108 "show_category_on_board": show_category_on_board,
109 "steps_on_board": steps_on_board,
110 "max_steps_on_board": max_steps_on_board,
111 }
112
113 if not Engine.is_editor_hint():
114 res["recent_file_count"] = recent_file_count
115 res["recent_files"] = recent_files
116 else:
117 res["editor_data_file_path"] = editor_data_file_path
118
119 res["internal_states"] = internal_states
120
121 return res
122
123
124func from_json(json: Dictionary) -> void:
125 if json.has("show_description_preview"):
126 show_description_preview = json["show_description_preview"]
127 if json.has("warn_about_empty_deletion"):
128 warn_about_empty_deletion = json["warn_about_empty_deletion"]
129 if json.has("edit_step_details_exclusively"):
130 edit_step_details_exclusively = json["edit_step_details_exclusively"]
131 if json.has("max_displayed_lines_in_description"):
132 max_displayed_lines_in_description = json["max_displayed_lines_in_description"]
133 if json.has("description_on_board"):
134 description_on_board = json["description_on_board"]
135 if json.has("editor_data_file_path"):
136 editor_data_file_path = json["editor_data_file_path"]
137 if json.has("recent_file_count"):
138 recent_file_count = json["recent_file_count"]
139 if json.has("recent_files"):
140 recent_files = PackedStringArray(json["recent_files"])
141 if json.has("show_steps_preview"):
142 show_steps_preview = json["show_steps_preview"]
143 if json.has("steps_on_board"):
144 steps_on_board = json["steps_on_board"]
145 if json.has("max_steps_on_board"):
146 max_steps_on_board = json["max_steps_on_board"]
147 if json.has("show_category_on_board"):
148 show_category_on_board = json["show_category_on_board"]
149 if json.has("internal_states"):
150 internal_states = json["internal_states"]
151 __notify_changed()
addons/kanban_tasks/data/settings.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://clempbpldb4hm
addons/kanban_tasks/data/stage.gd +0 -48
……@@ -1,48 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Data of a stage.
5
6
7var title: String:
8 set(value):
9 title = value
10 __notify_changed()
11
12var tasks: Array[String] = []:
13 get:
14 # Pass by value to avoid appending without emitting `changed`.
15 return tasks.duplicate()
16 set(value):
17 tasks = value
18 __notify_changed()
19
20
21func _init(p_title: String = "", p_tasks: Array[String] = []) -> void:
22 title = p_title
23 tasks = p_tasks
24 super._init()
25
26
27func to_json() -> Dictionary:
28 return {
29 "title": title,
30 "tasks": tasks,
31 }
32
33
34func from_json(json: Dictionary) -> void:
35 if json.has("title"):
36 title = json["title"]
37 else:
38 push_warning("Loading incomplete json data which is missing a title.")
39
40 if json.has("tasks"):
41 # HACK: Workaround for casting to typed array.
42 var s: Array[String] = []
43 for i in json["tasks"]:
44 s.append(i)
45
46 tasks = s
47 else:
48 push_warning("Loading incomplete json data which is missing a list of tasks.")
addons/kanban_tasks/data/stage.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://cc0hhlpqohqhw
addons/kanban_tasks/data/step.gd +0 -40
……@@ -1,40 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Data of a step.
5
6
7var details: String:
8 set(value):
9 details = value
10 __notify_changed()
11
12var done: bool:
13 set(value):
14 done = value
15 __notify_changed()
16
17
18func _init(p_details: String = "", p_done: bool = false) -> void:
19 details = p_details
20 done = p_done
21 super._init()
22
23
24func to_json() -> Dictionary:
25 return {
26 "details": details,
27 "done": done,
28 }
29
30
31func from_json(json: Dictionary) -> void:
32 if json.has("details"):
33 details = json["details"]
34 else:
35 push_warning("Loading incomplete json data which is missing details.")
36
37 if json.has("done"):
38 done = json["done"]
39 else:
40 push_warning("Loading incomplete json data which is missing 'done'.")
addons/kanban_tasks/data/step.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://cljwip0hl3d7e
addons/kanban_tasks/data/task.gd +0 -86
……@@ -1,86 +0,0 @@
1@tool
2extends "kanban_resource.gd"
3
4## Data of a task.
5
6
7const __Step := preload("step.gd")
8
9var title: String:
10 set(value):
11 title = value
12 __notify_changed()
13
14var description: String:
15 set(value):
16 description = value
17 __notify_changed()
18
19var category: String:
20 set(value):
21 category = value
22 __notify_changed()
23
24var steps: Array[__Step]:
25 get:
26 return steps.duplicate()
27 set(value):
28 steps = value
29 __notify_changed()
30
31
32func _init(p_title: String = "", p_description: String = "", p_category: String = "", p_steps: Array[__Step] = []) -> void:
33 title = p_title
34 description = p_description
35 category = p_category
36 steps = p_steps
37 super._init()
38
39
40func add_step(step: __Step, silent: bool = false) -> void:
41 var new_steps = steps
42 new_steps.append(step)
43 steps = new_steps
44 step.changed.connect(__notify_changed)
45 if not silent:
46 __notify_changed()
47
48
49func to_json() -> Dictionary:
50 var s: Array[Dictionary] = []
51 for step in steps:
52 s.append(step.to_json())
53
54 return {
55 "title": title,
56 "description": description,
57 "category": category,
58 "steps": s,
59 }
60
61
62func from_json(json: Dictionary) -> void:
63 if json.has("title"):
64 title = json["title"]
65 else:
66 push_warning("Loading incomplete json data which is missing a title.")
67
68 if json.has("description"):
69 description = json["description"]
70 else:
71 push_warning("Loading incomplete json data which is missing a description.")
72
73 if json.has("category"):
74 category = json["category"]
75 else:
76 push_warning("Loading incomplete json data which is missing a category.")
77
78 if json.has("steps"):
79 var s: Array[__Step] = []
80 for step in json["steps"]:
81 s.append(__Step.new())
82 s[-1].from_json(step)
83 s[-1].changed.connect(__notify_changed)
84 steps = s
85 else:
86 push_warning("Loading incomplete json data which is missing steps.")
addons/kanban_tasks/data/task.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://c6a4ohvkhg1yy
addons/kanban_tasks/edit_label/edit_label.gd +0 -192
……@@ -1,192 +0,0 @@
1@tool
2extends VBoxContainer
3
4## A label with editable content.
5##
6## While not editing the text is displayed in a label. So it does not stick out
7## of some layout to much. Only while editing the label it is replaced with an
8## line edit.
9## Click onto the label to start editing it or start the editing mode via code
10## by using [member show_edit]. When editing press [kbd]Enter[/kbd] to finish
11## editing or press [kbd]Esc[/kbd] to discard your changes.
12
13
14## Emitted when the text changed.
15##
16## [b]Note:[/b] This is only emitted when you confirm your editing by pressing
17## [kbd]Enter[/kbd]. If you need access to all changes while editing use the
18## line edit directly. You can get it by calling [method get_edit].
19signal text_changed(new_text: String)
20
21## The intentions with which the label can be edited.
22enum INTENTION {
23 REPLACE, ## The text will be marked completly when editing.
24 ADDITION, ## The cursor is placed at the end when editing.
25}
26
27## The text to display and edit.
28@export var text: String = "":
29 set(value):
30 text = value
31 __update_content()
32 text_changed.emit(text)
33
34## The default intention when editing the [member text].
35@export var default_intention := INTENTION.ADDITION
36
37## Whether a double click is needed for editing. If [code]false[/code] a single
38## click is enough.
39@export var double_click: bool = true
40
41# The line edit which is used to edit the text.
42var __edit: LineEdit
43
44# The label which is used to display the text.
45var __label: Label
46
47# The node which had focus before editing started. Used
48# to give focus back to it, when [kbd]Enter[/kbd] is used.
49var __old_focus: Control = null
50
51
52func _ready() -> void:
53 alignment = BoxContainer.ALIGNMENT_CENTER
54 mouse_filter = Control.MOUSE_FILTER_PASS
55
56 # Setup the internal label.
57 __label = Label.new()
58 __label.size_flags_horizontal = SIZE_EXPAND_FILL
59 __label.size_flags_vertical = SIZE_SHRINK_CENTER
60 __label.mouse_filter = Control.MOUSE_FILTER_PASS
61
62 __label.clip_text = true
63 __label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
64
65 __label.gui_input.connect(__on_label_gui_input)
66 add_child(__label)
67
68 # Setup the internal line edit.
69 __edit = LineEdit.new()
70 __edit.visible = false
71 __edit.size_flags_horizontal = SIZE_EXPAND_FILL
72 __edit.size_flags_vertical = SIZE_FILL
73 __edit.text_submitted.connect(__on_edit_text_submitted)
74 __edit.gui_input.connect(__on_edit_gui_input)
75 __edit.focus_exited.connect(__on_edit_focus_exited, CONNECT_DEFERRED)
76 add_child(__edit)
77
78 __update_content()
79
80 # Wait for the label to get its true size.
81 await get_tree().create_timer(0.0).timeout
82 # Keep the same size when changing the edit mode.
83 custom_minimum_size.y = max(__label.size.y, __edit.size.y) * 1.1
84
85
86func _input(event: InputEvent) -> void:
87 # End the editing when somewhere else was clicked.
88 if (event is InputEventMouseButton) and event.pressed and __edit.visible:
89 var local = __edit.make_input_local(event)
90 if not Rect2(Vector2.ZERO, __edit.size).has_point(local.position):
91 show_label()
92
93
94## Start editing the text and pass an optional intention.
95## This can be used to open the edit interface via code.
96func show_edit(intention: INTENTION = default_intention) -> void:
97 if __edit.visible:
98 return
99
100 # When this node can grab focus the focus should not be given back to the
101 # old focus owner.
102 __old_focus = get_viewport().gui_get_focus_owner() if focus_mode == FOCUS_NONE else null
103
104 __update_content()
105 __label.visible = false
106 __edit.visible = true
107
108 __edit.grab_focus()
109
110 match intention:
111 INTENTION.ADDITION:
112 __edit.caret_column = len(__edit.text)
113 INTENTION.REPLACE:
114 __edit.select_all()
115
116
117## Ends editing. If [code]apply_changes[/code] is [code]true[/code] the changed
118## text will be applied to the own [member text]. Otherwise the changes will
119## be discarded.
120func show_label(apply_changes: bool = true) -> void:
121 if __label.visible:
122 return
123
124 if apply_changes:
125 text = __edit.text
126
127 if is_instance_valid(__old_focus):
128 __old_focus.grab_focus()
129 else:
130 if focus_mode == FOCUS_NONE:
131 __edit.release_focus()
132 else:
133 grab_focus()
134
135 __edit.visible = false
136 __label.visible = true
137
138
139## Returns the [LineEdit] used to edit the text.
140##
141## [b]Warning:[/b] This is a required internal node, romoving and freeing it
142## may cause a crash. Feel free to edit its parameters to change, how the
143## [member text] is displayed.
144func get_edit() -> LineEdit:
145 return __edit
146
147
148## Returns the [Label] used display the text.
149##
150## [b]Warning:[/b] This is a required internal node, romoving and freeing it
151## may cause a crash. Feel free to edit its parameters to change, how the
152## [member text] is displayed.
153func get_label() -> Label:
154 return __label
155
156
157# Updates the diplayed text of [member __edit] and
158# [member __label] based on [member text].
159func __update_content() -> void:
160 if __label:
161 __label.text = text
162 if __edit:
163 __edit.text = text
164
165
166func __on_label_gui_input(event: InputEvent) -> void:
167 # Edit when the label is clicked.
168 if event is InputEventMouseButton:
169 if event.is_pressed() and event.button_index == MOUSE_BUTTON_LEFT:
170 if double_click == event.is_double_click():
171 # Mark event as handled.
172 __label.accept_event()
173 show_edit()
174
175
176func __on_edit_gui_input(event: InputEvent) -> void:
177 # Discard changes if ui_cancel action is pressed.
178 if event is InputEventKey and event.is_pressed():
179 if event.is_action(&"ui_cancel"):
180 show_label(false)
181
182
183func __on_edit_text_submitted(_new_text: String) -> void:
184 # For some reason line edit does not accept the event on its own in GD4.
185 __edit.accept_event()
186 show_label()
187
188
189func __on_edit_focus_exited() -> void:
190 if __edit.visible:
191 __old_focus = null
192 show_label()
addons/kanban_tasks/edit_label/edit_label.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://bj3jwa1phfj3t
addons/kanban_tasks/expand_button/expand_button.gd +0 -39
……@@ -1,39 +0,0 @@
1@tool
2extends Button
3
4
5signal state_changed(expanded: bool)
6
7@export var expanded: bool = true:
8 set(value):
9 if value != expanded:
10 expanded = value
11 __update_icon()
12 state_changed.emit(expanded)
13
14var __texture_rect := TextureRect.new()
15
16
17func _init() -> void:
18 focus_mode = Control.FOCUS_NONE
19 flat = true
20 var center := CenterContainer.new()
21 center.set_anchors_preset(Control.PRESET_FULL_RECT)
22 add_child(center)
23 center.add_child(__texture_rect)
24 pressed.connect(__on_pressed)
25 text = " "
26 __update_icon()
27
28
29func _notification(what) -> void:
30 if what == NOTIFICATION_THEME_CHANGED:
31 __texture_rect.texture = get_theme_icon(&"Collapse", &"EditorIcons")
32
33
34func __update_icon() -> void:
35 __texture_rect.flip_v = expanded
36
37
38func __on_pressed() -> void:
39 expanded = !expanded
addons/kanban_tasks/expand_button/expand_button.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://dyga42pgcqrsl
addons/kanban_tasks/icon.svg +0 -33
……@@ -1,33 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<svg
3 id="svg4"
4 version="1.1"
5 width="128"
6 viewBox="0 0 128 128"
7 height="128"
8 xmlns="http://www.w3.org/2000/svg"
9 xmlns:svg="http://www.w3.org/2000/svg"
10 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
11 xmlns:cc="http://creativecommons.org/ns#"
12 xmlns:dc="http://purl.org/dc/elements/1.1/">
13 <style
14 id="style833" />
15 <metadata
16 id="metadata10">
17 <rdf:RDF>
18 <cc:Work
19 rdf:about="">
20 <dc:format>image/svg+xml</dc:format>
21 <dc:type
22 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
23 </cc:Work>
24 </rdf:RDF>
25 </metadata>
26 <defs
27 id="defs8" />
28 <path
29 id="path2"
30 fill="#e0e0e0"
31 style="display:inline;fill-opacity:1;stroke-width:1"
32 d="m 124.42825,124.42808 c 1.04925,-1.04923 0.52463,-3.67233 -0.52461,-8.91851 l -5.2462,-26.230876 c -1.04923,-5.246181 -1.12134,-5.44896 -4.19694,-8.393751 L 42.06322,8.4876541 C 37.413004,3.8374388 29.925632,3.8374601 25.275439,8.4876504 L 8.4876601,25.275428 c -4.650214,4.650214 -4.650214,12.137563 5e-6,16.787778 L 80.88496,114.46034 c 3.147711,3.14772 3.147711,3.14772 8.393892,4.19694 l 26.230888,5.24619 c 5.24617,1.04921 7.86929,1.57385 8.91851,0.52461 z M 88.229612,96.623329 c -2.31791,2.317909 -6.075982,2.317912 -8.393892,-8e-6 L 46.260169,63.047932 c -2.317902,-2.317902 -2.317917,-6.075987 -7e-6,-8.393897 2.317904,-2.317902 6.075992,-2.317892 8.393895,9e-6 l 33.575566,33.575408 c 2.317898,2.317898 2.317898,6.075975 -1.1e-5,8.393877 z" />
33</svg>
addons/kanban_tasks/icon.svg.import +0 -38
……@@ -1,38 +0,0 @@
1[remap]
2
3importer="texture"
4type="CompressedTexture2D"
5uid="uid://bc22jf62qsikb"
6path="res://.godot/imported/icon.svg-055e2d1e864a196c183290847b905009.ctex"
7metadata={
8"has_editor_variant": true,
9"vram_texture": false
10}
11
12[deps]
13
14source_file="res://addons/kanban_tasks/icon.svg"
15dest_files=["res://.godot/imported/icon.svg-055e2d1e864a196c183290847b905009.ctex"]
16
17[params]
18
19compress/mode=0
20compress/high_quality=false
21compress/lossy_quality=0.7
22compress/hdr_compression=1
23compress/normal_map=0
24compress/channel_pack=0
25mipmaps/generate=false
26mipmaps/limit=-1
27roughness/mode=0
28roughness/src_normal=""
29process/fix_alpha_border=true
30process/premult_alpha=false
31process/normal_map_invert_y=false
32process/hdr_as_srgb=false
33process/hdr_clamp_exposure=false
34process/size_limit=0
35detect_3d/compress_to=1
36svg/scale=0.125
37editor/scale_with_editor_scale=true
38editor/convert_colors_with_editor_theme=true
addons/kanban_tasks/plugin.cfg +0 -7
……@@ -1,7 +0,0 @@
1[plugin]
2
3name="Kanban Tasks - Todo Manager"
4description="Another kanban board plugin for the godot engine."
5author="HolonProduction"
6version="2.0"
7script="plugin.gd"
addons/kanban_tasks/plugin.gd +0 -441
……@@ -1,441 +0,0 @@
1@tool
2extends "standalone_plugin.gd"
3
4
5const __Singletons := preload("./plugin_singleton/singletons.gd")
6const __Shortcuts := preload("./view/shortcuts.gd")
7const __EditContext := preload("./view/edit_context.gd")
8const __Settings := preload("./data/settings.gd")
9const __BoardData := preload("./data/board.gd")
10const __LayoutData := preload("./data/layout.gd")
11const __TaskData := preload("./data/task.gd")
12const __CategoryData := preload("./data/category.gd")
13const __StageData := preload("./data/stage.gd")
14const __BoardView := preload("./view/board/board.tscn")
15const __BoardViewType := preload("./view/board/board.gd")
16const __StartView := preload("./view/start/start.tscn")
17const __StartViewType := preload("./view/start/start.gd")
18const __DocumentationView := preload("./view/documentation/documentation.tscn")
19
20const SETTINGS_KEY: String = "kanban_tasks/general/settings"
21
22enum {
23 ACTION_SAVE,
24 ACTION_SAVE_AS,
25 ACTION_OPEN,
26 ACTION_CREATE,
27 ACTION_CLOSE,
28 ACTION_DOCUMENTATION,
29 ACTION_QUIT,
30}
31
32var main_panel_frame: MarginContainer
33var start_view: __StartViewType
34var file_dialog_save: FileDialog
35var file_dialog_open: FileDialog
36var discard_changes_dialog: ConfirmationDialog
37var documentation_dialog: AcceptDialog
38
39var file_menu: PopupMenu
40var help_menu: PopupMenu
41
42var board_view: __BoardViewType
43var board_label: Label
44var board_path: String = "":
45 set(value):
46 board_path = value
47 __update_board_label()
48 __update_menus()
49var board_changed: bool = false:
50 set(value):
51 board_changed = value
52 __update_board_label()
53
54
55func _enter_tree() -> void:
56 board_label = Label.new()
57 if not Engine.is_editor_hint():
58 add_control_to_container(CONTAINER_TOOLBAR, board_label)
59
60 file_menu = PopupMenu.new()
61 file_menu.name = "File"
62 file_menu.add_item("Save board", ACTION_SAVE)
63 file_menu.add_item("Save board as...", ACTION_SAVE_AS)
64 file_menu.add_item("Close board", ACTION_CLOSE)
65 file_menu.add_item("Open board...", ACTION_OPEN)
66 file_menu.add_item("Create board", ACTION_CREATE)
67 file_menu.add_separator()
68 file_menu.add_item("Quit", ACTION_QUIT)
69 file_menu.id_pressed.connect(__action)
70 add_menu(file_menu)
71
72 help_menu = PopupMenu.new()
73 help_menu.name = "Help"
74 help_menu.add_item("Documentation", ACTION_DOCUMENTATION)
75 help_menu.id_pressed.connect(__action)
76 add_menu(help_menu)
77
78 file_dialog_save = FileDialog.new()
79 file_dialog_save.access = FileDialog.ACCESS_FILESYSTEM
80 file_dialog_save.file_mode = FileDialog.FILE_MODE_SAVE_FILE
81 file_dialog_save.add_filter("*.kanban, *.json", "Kanban Board")
82 file_dialog_save.min_size = Vector2(800, 500)
83 file_dialog_save.file_selected.connect(__save_board)
84 get_editor_interface().get_base_control().add_child(file_dialog_save)
85
86 file_dialog_open = FileDialog.new()
87 file_dialog_open.access = FileDialog.ACCESS_FILESYSTEM
88 file_dialog_open.file_mode = FileDialog.FILE_MODE_OPEN_FILE
89 file_dialog_open.add_filter("*.kanban, *.json", "Kanban Board")
90 file_dialog_open.min_size = Vector2(800, 500)
91 file_dialog_open.file_selected.connect(__open_board)
92 get_editor_interface().get_base_control().add_child(file_dialog_open)
93
94 discard_changes_dialog = ConfirmationDialog.new()
95 discard_changes_dialog.dialog_text = "All unsaved changes will be discarded."
96 discard_changes_dialog.unresizable = true
97 get_editor_interface().get_base_control().add_child(discard_changes_dialog)
98
99 documentation_dialog = __DocumentationView.instantiate()
100 get_editor_interface().get_base_control().add_child(documentation_dialog)
101
102 main_panel_frame = MarginContainer.new()
103 main_panel_frame.add_theme_constant_override(&"margin_top", 5)
104 main_panel_frame.add_theme_constant_override(&"margin_left", 5)
105 main_panel_frame.add_theme_constant_override(&"margin_bottom", 5)
106 main_panel_frame.add_theme_constant_override(&"margin_right", 5)
107 main_panel_frame.size_flags_vertical = Control.SIZE_EXPAND_FILL
108 get_editor_interface().get_editor_main_screen().add_child(main_panel_frame)
109
110 start_view = __StartView.instantiate()
111 start_view.create_board.connect(__action.bind(ACTION_CREATE))
112 start_view.open_board.connect(__on_start_view_open_board)
113 main_panel_frame.add_child(start_view)
114
115 _make_visible(false)
116
117 await get_tree().create_timer(0.0).timeout
118
119 __load_settings()
120
121 if Engine.is_editor_hint():
122 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
123 var editor_data_file_path = ctx.settings.editor_data_file_path
124 if FileAccess.file_exists(editor_data_file_path):
125 __open_board(editor_data_file_path)
126 elif FileAccess.file_exists("res://addons/kanban_tasks/data.json"):
127 # TODO: Remove sometime in the future.
128 # Migrate from old version.
129 __open_board("res://addons/kanban_tasks/data.json")
130 __save_board(editor_data_file_path)
131 else:
132 __create_board()
133 __save_board(editor_data_file_path)
134
135 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
136 ctx.save_board.connect(__editor_save_board)
137 ctx.reload_board.connect(__editor_reload_board)
138 ctx.create_board.connect(__editor_create_board)
139
140 __update_menus()
141
142
143func _exit_tree() -> void:
144 if not Engine.is_editor_hint():
145 remove_control_from_container(CONTAINER_TOOLBAR, board_label)
146 board_label.queue_free()
147
148 remove_menu(file_menu)
149 file_menu.queue_free()
150
151 remove_menu(help_menu)
152 file_menu.queue_free()
153
154 file_dialog_save.queue_free()
155 file_dialog_open.queue_free()
156 discard_changes_dialog.queue_free()
157 documentation_dialog.queue_free()
158
159 main_panel_frame.queue_free()
160 start_view.queue_free()
161
162 if is_instance_valid(board_view):
163 board_view.queue_free()
164
165
166func _shortcut_input(event: InputEvent) -> void:
167 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
168 if not Engine.is_editor_hint() and shortcuts.save.matches_event(event):
169 get_viewport().set_input_as_handled()
170 __action(ACTION_SAVE)
171
172 if not Engine.is_editor_hint() and shortcuts.save_as.matches_event(event):
173 get_viewport().set_input_as_handled()
174 __action(ACTION_SAVE_AS)
175
176
177func _has_main_screen() -> bool:
178 return true
179
180
181func _make_visible(visible) -> void:
182 if main_panel_frame:
183 main_panel_frame.visible = visible
184
185
186func _get_plugin_name() -> String:
187 return "Tasks"
188
189
190func _get_plugin_icon() -> Texture2D:
191 return preload("./icon.svg")
192
193
194func _notification(what: int) -> void:
195 match what:
196 NOTIFICATION_WM_CLOSE_REQUEST:
197 if not Engine.is_editor_hint():
198 if not board_changed:
199 get_tree().quit()
200 else:
201 __request_discard_changes(get_tree().quit)
202
203
204func __update_menus() -> void:
205 if not is_instance_valid(file_menu):
206 return
207
208 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
209
210 file_menu.set_item_disabled(
211 file_menu.get_item_index(ACTION_SAVE),
212 not is_instance_valid(board_view),
213 )
214 file_menu.set_item_shortcut(
215 file_menu.get_item_index(ACTION_SAVE),
216 shortcuts.save,
217 )
218 file_menu.set_item_disabled(
219 file_menu.get_item_index(ACTION_SAVE_AS),
220 not is_instance_valid(board_view),
221 )
222 file_menu.set_item_shortcut(
223 file_menu.get_item_index(ACTION_SAVE_AS),
224 shortcuts.save_as,
225 )
226 file_menu.set_item_disabled(
227 file_menu.get_item_index(ACTION_CLOSE),
228 not is_instance_valid(board_view),
229 )
230
231
232func __update_board_label() -> void:
233 if not is_instance_valid(board_label):
234 return
235 if is_instance_valid(board_view):
236 if board_path.is_empty():
237 board_label.text = "unsaved"
238 else:
239 board_label.text = board_path
240 if board_changed:
241 board_label.text += "*"
242 else:
243 board_label.text = ""
244
245
246func __action(id: int) -> void:
247 match id:
248 ACTION_SAVE:
249 __request_save()
250 ACTION_SAVE_AS:
251 __request_save(true)
252 ACTION_CREATE:
253 if not board_changed:
254 __create_board()
255 else:
256 __request_discard_changes(__create_board)
257 ACTION_OPEN:
258 if not board_changed:
259 file_dialog_open.popup_centered()
260 else:
261 __request_discard_changes(file_dialog_open.popup_centered)
262 ACTION_CLOSE:
263 if not board_changed:
264 __close_board()
265 else:
266 __request_discard_changes(__close_board)
267 ACTION_DOCUMENTATION:
268 documentation_dialog.popup_centered()
269 ACTION_QUIT:
270 get_tree().get_root().propagate_notification(NOTIFICATION_WM_CLOSE_REQUEST)
271
272
273func __editor_save_board() -> void:
274 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
275 __save_board(ctx.settings.editor_data_file_path)
276
277
278func __editor_reload_board() -> void:
279 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
280 __action(ACTION_SAVE)
281 __open_board(ctx.settings.editor_data_file_path)
282
283
284func __editor_create_board() -> void:
285 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
286 __action(ACTION_SAVE)
287 __create_board()
288 __save_board(ctx.settings.editor_data_file_path)
289
290
291func __request_discard_changes(callback: Callable) -> void:
292 for connection in discard_changes_dialog.confirmed.get_connections():
293 discard_changes_dialog.confirmed.disconnect(connection["callable"])
294 discard_changes_dialog.confirmed.connect(callback)
295 discard_changes_dialog.popup_centered()
296
297
298func __request_save(force_new_location: bool = false) -> void:
299 if not is_instance_valid(board_view):
300 return
301 if not force_new_location and not board_path.is_empty():
302 __save_board(board_path)
303 else:
304 file_dialog_save.popup_centered()
305
306
307func __create_board() -> void:
308 var data := __BoardData.new()
309
310 data.layout = __LayoutData.new([
311 PackedStringArray([data.add_stage(__StageData.new("Todo"))]),
312 PackedStringArray([data.add_stage(__StageData.new("Doing"))]),
313 PackedStringArray([data.add_stage(__StageData.new("Done"))]),
314 ])
315 data.add_category(
316 __CategoryData.new(
317 "Task",
318 get_editor_interface().get_base_control().
319 get_theme_color(&"accent_color", &"Editor")
320 )
321 )
322
323 data.changed.connect(__on_board_changed)
324
325 __make_board_view_visible(data)
326
327 board_path = ""
328 board_changed = false
329
330
331func __save_board(path: String) -> void:
332 if is_instance_valid(board_view):
333 __add_to_recent_files(path)
334 board_path = path
335 board_view.board_data.save(path)
336 board_changed = false
337
338
339func __open_board(path: String) -> void:
340 var data := __BoardData.new()
341 data.load(path)
342 data.changed.connect(__on_board_changed)
343
344 __make_board_view_visible(data)
345 __add_to_recent_files(path)
346
347 board_path = path
348 board_changed = false
349
350
351func __close_board() -> void:
352 board_view.queue_free()
353 board_view = null
354 board_path = ""
355 board_changed = false
356 start_view.show()
357
358
359func __add_to_recent_files(path: String) -> void:
360 if Engine.is_editor_hint():
361 return
362
363 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
364 var files = ctx.settings.recent_files
365
366 if path in files:
367 files.remove_at(files.find(path))
368 files.insert(0, path)
369 else:
370 files.insert(0, path)
371 files.resize(ctx.settings.recent_file_count)
372
373 ctx.settings.recent_files = files
374
375
376func __on_board_changed() -> void:
377 board_changed = true
378 if Engine.is_editor_hint():
379 __request_save()
380
381
382func __on_start_view_open_board(path: String) -> void:
383 if path.is_empty():
384 __action(ACTION_OPEN)
385 else:
386 __open_board(path)
387
388
389func __make_board_view_visible(data: __BoardData) -> void:
390 if is_instance_valid(board_view):
391 board_view.queue_free()
392 board_view = __BoardView.instantiate()
393 board_view.show_documentation.connect(__action.bind(ACTION_DOCUMENTATION))
394 board_view.board_data = data
395
396 main_panel_frame.add_child(board_view)
397 start_view.hide()
398
399
400func __save_settings() -> void:
401 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
402 var data := JSON.stringify(ctx.settings.to_json())
403 if Engine.is_editor_hint():
404 get_editor_interface().get_editor_settings().set_setting(
405 SETTINGS_KEY,
406 data,
407 )
408 else:
409 ProjectSettings.set_setting(
410 SETTINGS_KEY,
411 data,
412 )
413 save_project_settings()
414
415
416func __load_settings() -> void:
417 var data: String = "{}"
418 if Engine.is_editor_hint():
419 var editor_settings = get_editor_interface().get_editor_settings()
420 if editor_settings.has_setting(SETTINGS_KEY):
421 data = editor_settings.get_setting(SETTINGS_KEY)
422 else:
423 if ProjectSettings.has_setting(SETTINGS_KEY):
424 data = ProjectSettings.get_setting(SETTINGS_KEY)
425
426 var json = JSON.new()
427 var err = json.parse(data)
428 if err != OK:
429 push_error(
430 "Error "
431 + str(err)
432 + " while parsing settings. At line "
433 + str(json.get_error_line())
434 + " the following problem occured:\n"
435 + json.get_error_message()
436 )
437 return
438
439 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
440 ctx.settings.from_json(json.data)
441 ctx.settings.changed.connect(__save_settings)
addons/kanban_tasks/plugin.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://bp8y3h81yydrb
addons/kanban_tasks/plugin_singleton/singletons.gd +0 -23
……@@ -1,23 +0,0 @@
1extends Object
2
3## Allows the registration of anonymous singletons into the scene tree.
4
5
6const HOLDER_NAME: String = "PluginSingletons"
7
8
9static func instance_of(p_script: Script, requester: Node) -> Variant:
10 var holder: Node = requester.get_tree().get_root().get_node_or_null(HOLDER_NAME)
11
12 if not is_instance_valid(holder):
13 holder = Node.new()
14 holder.name = HOLDER_NAME
15 requester.get_tree().get_root().add_child(holder)
16
17 for child in holder.get_children():
18 if child.get_script() == p_script:
19 return child
20
21 var instance: Node = p_script.new()
22 holder.add_child(instance)
23 return instance
addons/kanban_tasks/plugin_singleton/singletons.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://l7m3xlrfetoy
addons/kanban_tasks/standalone_plugin.gd +0 -37
……@@ -1,37 +0,0 @@
1#@standalone
2# The line above is needed for standalone detection. Do not modify it.
3@tool
4extends EditorPlugin
5
6## The StandalonePlugin implementation for in editor use.
7##
8## This file violates DRY. It should not contain references to other files
9## of standalone plugin. This allows the independent use as editor plugin.
10
11
12## Additional containers for add_control_to_container
13# CONTAINER_TOOLBAR = 0,
14# CONTAINER_SPATIAL_EDITOR_MENU = 1,
15# CONTAINER_SPATIAL_EDITOR_SIDE_LEFT = 2,
16# CONTAINER_SPATIAL_EDITOR_SIDE_RIGHT = 3,
17# CONTAINER_SPATIAL_EDITOR_BOTTOM = 4,
18# CONTAINER_CANVAS_EDITOR_MENU = 5,
19# CONTAINER_CANVAS_EDITOR_SIDE_LEFT = 6,
20# CONTAINER_CANVAS_EDITOR_SIDE_RIGHT = 7,
21# CONTAINER_CANVAS_EDITOR_BOTTOM = 8,
22# CONTAINER_INSPECTOR_BOTTOM = 9,
23# CONTAINER_PROJECT_SETTING_TAB_LEFT = 10,
24# CONTAINER_PROJECT_SETTING_TAB_RIGHT = 11,
25const CONTAINER_LAUNCH_PAD := 12
26
27
28func save_project_settings() -> int:
29 return ProjectSettings.save()
30
31
32func add_menu(menu: PopupMenu) -> void:
33 pass
34
35
36func remove_menu(menu: PopupMenu) -> void:
37 pass
addons/kanban_tasks/standalone_plugin.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://r51ncwjt2umm
addons/kanban_tasks/uuid/LICENSE +0 -21
……@@ -1,21 +0,0 @@
1MIT License
2
3Copyright (c) 2018 Xavier Sellier
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in all
13copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
addons/kanban_tasks/uuid/uuid.gd +0 -41
……@@ -1,41 +0,0 @@
1# Note: The code might not be as pretty it could be, since it's written
2# in a way that maximizes performance. Methods are inlined and loops are avoided.
3extends Node
4
5const MODULO_8_BIT = 256
6
7static func getRandomInt():
8 # Randomize every time to minimize the risk of collisions
9 randomize()
10
11 return randi() % MODULO_8_BIT
12
13static func uuidbin():
14 # 16 random bytes with the bytes on index 6 and 8 modified
15 return [
16 getRandomInt(), getRandomInt(), getRandomInt(), getRandomInt(),
17 getRandomInt(), getRandomInt(), ((getRandomInt()) & 0x0f) | 0x40, getRandomInt(),
18 ((getRandomInt()) & 0x3f) | 0x80, getRandomInt(), getRandomInt(), getRandomInt(),
19 getRandomInt(), getRandomInt(), getRandomInt(), getRandomInt(),
20 ]
21
22static func v4():
23 # 16 random bytes with the bytes on index 6 and 8 modified
24 var b = uuidbin()
25
26 return '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x' % [
27 # low
28 b[0], b[1], b[2], b[3],
29
30 # mid
31 b[4], b[5],
32
33 # hi
34 b[6], b[7],
35
36 # clock
37 b[8], b[9],
38
39 # clock
40 b[10], b[11], b[12], b[13], b[14], b[15]
41 ]
addons/kanban_tasks/uuid/uuid.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://dvosrae54se4x
addons/kanban_tasks/view/board/board.gd +0 -156
……@@ -1,156 +0,0 @@
1@tool
2extends VBoxContainer
3
4## The visual representation of a kanban board.
5
6
7const __Singletons := preload("../../plugin_singleton/singletons.gd")
8const __Shortcuts := preload("../shortcuts.gd")
9const __EditContext := preload("../edit_context.gd")
10const __BoardData := preload("../../data/board.gd")
11const __StageScript := preload("../stage/stage.gd")
12const __StageScene := preload("../stage/stage.tscn")
13const __Filter := preload("../filter.gd")
14const __SettingsScript := preload("../settings/settings.gd")
15
16signal show_documentation()
17
18var board_data: __BoardData
19
20@onready var search_bar: LineEdit = %SearchBar
21@onready var button_advanced_search: Button = %AdvancedSearch
22@onready var button_show_categories: Button = %ShowCategories
23@onready var button_show_descriptions: Button = %ShowDescriptions
24@onready var button_show_steps: Button = %ShowSteps
25@onready var button_documentation: Button = %Documentation
26@onready var button_settings: Button = %Settings
27@onready var column_holder: HBoxContainer = %ColumnHolder
28@onready var settings: __SettingsScript = %SettingsView
29
30
31func _ready():
32 update()
33 board_data.layout.changed.connect(update)
34
35 settings.board_data = board_data
36
37 search_bar.text_changed.connect(__on_filter_changed)
38 search_bar.text_submitted.connect(__on_search_bar_entered)
39 button_advanced_search.toggled.connect(__on_filter_changed)
40
41 button_show_categories.toggled.connect(__on_show_categories_toggled)
42 button_show_descriptions.toggled.connect(__on_show_descriptions_toggled)
43 button_show_steps.toggled.connect(__on_show_steps_toggled)
44
45 notification(NOTIFICATION_THEME_CHANGED)
46
47 await get_tree().create_timer(0.0).timeout
48 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
49
50 ctx.settings.changed.connect(update)
51
52 ctx.filter_changed.connect(__on_filter_changed_external)
53
54 button_documentation.pressed.connect(func(): show_documentation.emit())
55 button_documentation.visible = Engine.is_editor_hint()
56
57 button_settings.pressed.connect(settings.popup_centered_ratio_no_fullscreen)
58
59
60func _shortcut_input(event: InputEvent) -> void:
61 if not __Shortcuts.should_handle_shortcut(self):
62 return
63 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
64 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
65 if not event.is_echo() and event.is_pressed():
66 if shortcuts.search.matches_event(event):
67 search_bar.grab_focus()
68 get_viewport().set_input_as_handled()
69 elif shortcuts.undo.matches_event(event):
70 ctx.undo_redo.undo()
71 get_viewport().set_input_as_handled()
72 elif shortcuts.redo.matches_event(event):
73 ctx.undo_redo.redo()
74 get_viewport().set_input_as_handled()
75
76
77func _notification(what):
78 match(what):
79 NOTIFICATION_THEME_CHANGED:
80 if is_instance_valid(search_bar):
81 search_bar.right_icon = get_theme_icon(&"Search", &"EditorIcons")
82 if is_instance_valid(button_settings):
83 button_settings.icon = get_theme_icon(&"Tools", &"EditorIcons")
84 if is_instance_valid(button_documentation):
85 button_documentation.icon = get_theme_icon(&"Help", &"EditorIcons")
86 if is_instance_valid(button_advanced_search):
87 button_advanced_search.icon = get_theme_icon(&"Zoom", &"EditorIcons")
88 if is_instance_valid(button_show_categories):
89 button_show_categories.icon = get_theme_icon(&"Rectangle", &"EditorIcons")
90 if is_instance_valid(button_show_descriptions):
91 button_show_descriptions.icon = get_theme_icon(&"Script", &"EditorIcons")
92 if is_instance_valid(button_show_steps):
93 button_show_steps.icon = get_theme_icon(&"FileList", &"EditorIcons")
94
95
96func update() -> void:
97 for column in column_holder.get_children():
98 column.queue_free()
99
100 for column_data in board_data.layout.columns:
101 var column_scroll = ScrollContainer.new()
102 column_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
103 column_scroll.set_v_size_flags(Control.SIZE_EXPAND_FILL)
104 column_scroll.set_h_size_flags(Control.SIZE_EXPAND_FILL)
105 var column = VBoxContainer.new()
106 column.set_v_size_flags(Control.SIZE_EXPAND_FILL)
107 column.set_h_size_flags(Control.SIZE_EXPAND_FILL)
108
109 column_scroll.add_child(column)
110 column_holder.add_child(column_scroll)
111
112 for uuid in column_data:
113 var stage := __StageScene.instantiate()
114 stage.board_data = board_data
115 stage.data_uuid = uuid
116 column.add_child(stage)
117
118 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
119 button_show_categories.set_pressed_no_signal(ctx.settings.show_category_on_board)
120 button_show_descriptions.set_pressed_no_signal(ctx.settings.show_description_preview)
121 button_show_steps.set_pressed_no_signal(ctx.settings.show_steps_preview)
122
123
124# Do not use parameters the method is bound to diffrent signals.
125func __on_filter_changed(param1: Variant = null):
126 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
127
128 if ctx.filter_changed.is_connected(__on_filter_changed_external):
129 ctx.filter_changed.disconnect(__on_filter_changed_external)
130
131 ctx.filter = __Filter.new(search_bar.text, button_advanced_search.button_pressed)
132
133 ctx.filter_changed.connect(__on_filter_changed_external)
134
135
136func __on_search_bar_entered(filter: String):
137 button_advanced_search.grab_focus()
138
139
140func __on_filter_changed_external():
141 search_bar.text = ""
142
143
144func __on_show_categories_toggled(button_pressed: bool):
145 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
146 ctx.settings.show_category_on_board = button_pressed
147
148
149func __on_show_descriptions_toggled(button_pressed: bool):
150 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
151 ctx.settings.show_description_preview = button_pressed
152
153
154func __on_show_steps_toggled(button_pressed: bool):
155 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
156 ctx.settings.show_steps_preview = button_pressed
addons/kanban_tasks/view/board/board.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://defjycflkd24g
addons/kanban_tasks/view/board/board.tscn +0 -89
……@@ -1,89 +0,0 @@
1[gd_scene load_steps=3 format=3 uid="uid://c5dk4lnyiag3w"]
2
3[ext_resource type="Script" uid="uid://defjycflkd24g" path="res://addons/kanban_tasks/view/board/board.gd" id="1_p7lf4"]
4[ext_resource type="PackedScene" uid="uid://dh1yunmhipirg" path="res://addons/kanban_tasks/view/settings/settings.tscn" id="2_by8mq"]
5
6[node name="BoardView" type="VBoxContainer"]
7anchors_preset = 15
8anchor_right = 1.0
9anchor_bottom = 1.0
10grow_horizontal = 2
11grow_vertical = 2
12size_flags_horizontal = 3
13size_flags_vertical = 3
14mouse_filter = 0
15theme_override_constants/separation = 5
16script = ExtResource("1_p7lf4")
17
18[node name="Header" type="HBoxContainer" parent="."]
19layout_mode = 2
20theme_override_constants/separation = 5
21
22[node name="SearchBar" type="LineEdit" parent="Header"]
23unique_name_in_owner = true
24layout_mode = 2
25size_flags_horizontal = 3
26placeholder_text = "Search"
27clear_button_enabled = true
28
29[node name="AdvancedSearch" type="Button" parent="Header"]
30unique_name_in_owner = true
31layout_mode = 2
32tooltip_text = "Search in details."
33toggle_mode = true
34
35[node name="VSeparator" type="VSeparator" parent="Header"]
36layout_mode = 2
37tooltip_text = "Show categories"
38theme_override_constants/separation = 0
39
40[node name="ShowCategories" type="Button" parent="Header"]
41unique_name_in_owner = true
42visible = false
43layout_mode = 2
44toggle_mode = true
45
46[node name="ShowDescriptions" type="Button" parent="Header"]
47unique_name_in_owner = true
48layout_mode = 2
49tooltip_text = "Show descriptions."
50toggle_mode = true
51
52[node name="ShowSteps" type="Button" parent="Header"]
53unique_name_in_owner = true
54layout_mode = 2
55tooltip_text = "Show steps."
56toggle_mode = true
57
58[node name="VSeparator2" type="VSeparator" parent="Header"]
59layout_mode = 2
60theme_override_constants/separation = 0
61
62[node name="Documentation" type="Button" parent="Header"]
63unique_name_in_owner = true
64layout_mode = 2
65tooltip_text = "Open documentation."
66flat = true
67
68[node name="Settings" type="Button" parent="Header"]
69unique_name_in_owner = true
70layout_mode = 2
71tooltip_text = "Manage board settings."
72
73[node name="ScrollContainer" type="ScrollContainer" parent="."]
74layout_mode = 2
75size_flags_vertical = 3
76mouse_filter = 0
77vertical_scroll_mode = 0
78
79[node name="ColumnHolder" type="HBoxContainer" parent="ScrollContainer"]
80unique_name_in_owner = true
81layout_mode = 2
82size_flags_horizontal = 3
83size_flags_vertical = 3
84theme_override_constants/separation = 5
85alignment = 1
86
87[node name="SettingsView" parent="." instance=ExtResource("2_by8mq")]
88unique_name_in_owner = true
89visible = false
addons/kanban_tasks/view/category/category_popup_menu.gd +0 -44
……@@ -1,44 +0,0 @@
1@tool
2extends PopupMenu
3
4
5const __BoardData := preload("../../data/board.gd")
6
7var board_data: __BoardData
8
9signal uuid_selected(uuid)
10
11
12func _init() -> void:
13 about_to_popup.connect(__update_items_from_board)
14 id_pressed.connect(__on_id_pressed)
15
16
17func popup_at_local_position(source: CanvasItem, local_position: Vector2) -> void:
18 popup_at_global_position(source, source.get_global_transform() * local_position)
19
20
21func popup_at_global_position(source: CanvasItem, global_position: Vector2) -> void:
22 position = global_position
23 if not source.get_window().gui_embed_subwindows:
24 position += source.get_window().position
25 popup()
26
27
28func popup_at_mouse_position(source: CanvasItem) -> void:
29 popup_at_global_position(source, source.get_global_mouse_position())
30
31
32func __update_items_from_board() -> void:
33 clear()
34 size = Vector2i.ZERO
35 for uuid in board_data.get_categories():
36 var i = Image.create(16, 16, false, Image.FORMAT_RGB8)
37 i.fill(board_data.get_category(uuid).color)
38 var t = ImageTexture.create_from_image(i)
39 add_icon_item(t, board_data.get_category(uuid).title)
40 set_item_metadata(-1, uuid)
41
42
43func __on_id_pressed(id) -> void:
44 uuid_selected.emit(get_item_metadata(id))
addons/kanban_tasks/view/category/category_popup_menu.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://c4fyjjklcwapk
addons/kanban_tasks/view/details/details.gd +0 -227
……@@ -1,227 +0,0 @@
1@tool
2extends AcceptDialog
3
4
5const __BoardData := preload("../../data/board.gd")
6const __StepData := preload("../../data/step.gd")
7const __StepEntry := preload("../details/step_entry.gd")
8const __Singletons := preload("../../plugin_singleton/singletons.gd")
9const __EditContext := preload("../edit_context.gd")
10
11var board_data: __BoardData
12var data_uuid: String
13
14var __step_data: __StepData
15
16@onready var category_select: OptionButton = %Category
17@onready var h_split_container: HSplitContainer = %HSplitContainer
18@onready var description_edit: TextEdit = %Description
19@onready var step_holder: VBoxContainer = %StepHolder
20@onready var steps_panel_container: PanelContainer = %PanelContainer
21@onready var create_step_edit: LineEdit = %CreateStepEdit
22@onready var step_details: VBoxContainer = %StepDetails
23@onready var close_step_details_button: Button = %CloseStepDetails
24@onready var step_edit: TextEdit = %StepEdit
25
26
27func _ready() -> void:
28 about_to_popup.connect(__on_about_to_popup)
29 create_step_edit.text_submitted.connect(__create_step)
30 close_step_details_button.pressed.connect(__close_step_details)
31 notification(NOTIFICATION_THEME_CHANGED)
32 step_holder.entry_action_triggered.connect(__on_step_action_triggered)
33 step_holder.entry_move_requesed.connect(__step_move_requesed)
34
35 visibility_changed.connect(__save_internal_state)
36
37
38func _notification(what: int) -> void:
39 match(what):
40 NOTIFICATION_THEME_CHANGED:
41 if is_instance_valid(steps_panel_container):
42 steps_panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Tree"))
43 if is_instance_valid(create_step_edit):
44 create_step_edit.right_icon = get_theme_icon(&"Add", &"EditorIcons")
45 if is_instance_valid(close_step_details_button):
46 close_step_details_button.icon = get_theme_icon(&"Close", &"EditorIcons")
47
48
49func update() -> void:
50 if description_edit.text_changed.is_connected(__on_description_changed):
51 description_edit.text_changed.disconnect(__on_description_changed)
52 if description_edit.text != board_data.get_task(data_uuid).description:
53 description_edit.text = board_data.get_task(data_uuid).description
54 description_edit.text_changed.connect(__on_description_changed)
55
56 title = "Task Details: " + board_data.get_task(data_uuid).title
57
58 if category_select.item_selected.is_connected(__on_category_selected):
59 category_select.item_selected.disconnect(__on_category_selected)
60 category_select.clear()
61 for uuid in board_data.get_categories():
62 var i = Image.create(16, 16, false, Image.FORMAT_RGB8)
63 i.fill(board_data.get_category(uuid).color)
64 var t = ImageTexture.create_from_image(i)
65 category_select.add_icon_item(t, board_data.get_category(uuid).title)
66 category_select.set_item_metadata(-1, uuid)
67 if uuid == board_data.get_task(data_uuid).category:
68 category_select.select(category_select.item_count - 1)
69
70 category_select.item_selected.connect(__on_category_selected)
71
72 step_holder.clear_steps()
73 for step in board_data.get_task(data_uuid).steps:
74 step_holder.add_step(step)
75 for entry in step_holder.get_step_entries():
76 entry.being_edited = (entry.step_data == __step_data)
77
78 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
79
80 step_details.visible = is_instance_valid(__step_data)
81 description_edit.visible = not (ctx.settings.edit_step_details_exclusively and is_instance_valid(__step_data))
82 if is_instance_valid(__step_data):
83 if step_edit.text_changed.is_connected(__on_step_details_changed):
84 step_edit.text_changed.disconnect(__on_step_details_changed)
85 step_edit.text = __step_data.details
86 step_edit.text_changed.connect(__on_step_details_changed)
87
88
89# Workaround for godotengine/godot#70451
90func popup_centered_ratio_no_fullscreen(ratio: float = 0.8) -> void:
91 var viewport: Viewport = get_parent().get_viewport()
92 popup(Rect2i(Vector2(viewport.position) + viewport.size / 2.0 - viewport.size * ratio / 2.0, viewport.size * ratio))
93
94
95func edit_step_details(step: __StepData) -> void:
96 if is_instance_valid(__step_data):
97 __step_data.changed.disconnect(update)
98 __step_data = step
99 __step_data.changed.connect(update)
100 update()
101 step_edit.set_caret_line(step_edit.get_line_count())
102 step_edit.set_caret_column(len(step_edit.get_line(step_edit.get_line_count() - 1)))
103 step_edit.grab_focus.call_deferred()
104
105
106func move_step_up(step: __StepData) -> void:
107 var steps = board_data.get_task(data_uuid).steps
108 if step in steps and steps[0] != step:
109 var index = steps.find(step)
110 steps.erase(step)
111 steps.insert(index - 1, step)
112 board_data.get_task(data_uuid).steps = steps
113 update()
114
115
116func move_step_down(step: __StepData) -> void:
117 var steps = board_data.get_task(data_uuid).steps
118 if step in steps and steps[-1] != step:
119 var index = steps.find(step)
120 steps.erase(step)
121 steps.insert(index + 1, step)
122 board_data.get_task(data_uuid).steps = steps
123 update()
124
125
126func delete_step(step: __StepData) -> void:
127 close_step_details(step)
128 var steps = board_data.get_task(data_uuid).steps
129 if step in steps:
130 steps.erase(step)
131 board_data.get_task(data_uuid).steps = steps
132 update()
133
134
135func close_step_details(step: __StepData) -> void:
136 if __step_data == step:
137 __close_step_details()
138
139
140func __on_step_action_triggered(entry: __StepEntry, action: __StepEntry.Actions) -> void:
141 match action:
142 __StepEntry.Actions.EDIT_HARD:
143 edit_step_details(entry.step_data)
144 __StepEntry.Actions.EDIT_SOFT:
145 if is_instance_valid(__step_data):
146 edit_step_details(entry.step_data)
147 __StepEntry.Actions.CLOSE:
148 close_step_details(entry.step_data)
149 __StepEntry.Actions.DELETE:
150 delete_step(entry.step_data)
151 __StepEntry.Actions.MOVE_UP:
152 move_step_up(entry.step_data)
153 __StepEntry.Actions.MOVE_DOWN:
154 move_step_down(entry.step_data)
155
156
157func __step_move_requesed(moved_entry: __StepEntry, target_entry: __StepEntry, move_after_target: bool) -> void:
158 var steps = board_data.get_task(data_uuid).steps
159 var moved_idx = steps.find(moved_entry.step_data)
160 var target_idx = steps.find(target_entry.step_data)
161 if moved_idx < 0 or target_idx < 0 or moved_idx == target_idx:
162 return
163 steps.erase(moved_entry.step_data)
164 if moved_idx < target_idx:
165 target_idx -= 1
166 if move_after_target:
167 steps.insert(target_idx + 1, moved_entry.step_data)
168 else:
169 steps.insert(target_idx, moved_entry.step_data)
170 board_data.get_task(data_uuid).steps = steps
171 update()
172
173
174func __load_internal_state() -> void:
175 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
176 if ctx.settings.internal_states.has("details_editor_step_holder_width"):
177 h_split_container.split_offset = ctx.settings.internal_states["details_editor_step_holder_width"]
178
179
180func __save_internal_state() -> void:
181 if not visible:
182 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
183 ctx.settings.set_internal_state("details_editor_step_holder_width", h_split_container.split_offset)
184
185
186func __close_step_details() -> void:
187 __step_data.changed.disconnect(update)
188 __step_data = null
189 update()
190
191
192func __on_step_details_changed() -> void:
193 if __step_data.changed.is_connected(update):
194 __step_data.changed.disconnect(update)
195 __step_data.details = step_edit.text
196 __step_data.changed.connect(update)
197
198
199func __on_about_to_popup() -> void:
200 if is_instance_valid(__step_data):
201 __close_step_details()
202 update()
203 __load_internal_state()
204 if board_data.get_task(data_uuid).description.is_empty():
205 description_edit.grab_focus.call_deferred()
206
207
208func __on_description_changed() -> void:
209 board_data.get_task(data_uuid).description = description_edit.text
210
211
212func __on_category_selected(index: int) -> void:
213 board_data.get_task(data_uuid).category = category_select.get_item_metadata(index)
214
215
216func __create_step(text: String) -> void:
217 if text.is_empty():
218 return
219 var task = board_data.get_task(data_uuid)
220 var data = __StepData.new(text)
221 task.add_step(data)
222 create_step_edit.text = ""
223 update()
224 if is_instance_valid(__step_data):
225 for step in step_holder.get_step_entries():
226 if step.step_data == data:
227 step.grab_focus.call_deferred()
addons/kanban_tasks/view/details/details.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://c5cqn5txafqim
addons/kanban_tasks/view/details/details.tscn +0 -112
……@@ -1,112 +0,0 @@
1[gd_scene load_steps=6 format=3 uid="uid://bwi22eyrmeeet"]
2
3[ext_resource type="Script" uid="uid://c5cqn5txafqim" path="res://addons/kanban_tasks/view/details/details.gd" id="1_gh7s6"]
4[ext_resource type="PackedScene" uid="uid://dwjg5vyxx4g48" path="res://addons/kanban_tasks/view/details/step_holder.tscn" id="2_0ptaf"]
5
6[sub_resource type="Image" id="Image_bpcvf"]
7data = {
8"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
9"format": "RGBA8",
10"height": 16,
11"mipmaps": false,
12"width": 16
13}
14
15[sub_resource type="ImageTexture" id="ImageTexture_op8g0"]
16image = SubResource("Image_bpcvf")
17
18[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_g2k57"]
19content_margin_left = 4.0
20content_margin_top = 4.0
21content_margin_right = 4.0
22content_margin_bottom = 5.0
23bg_color = Color(0.1, 0.1, 0.1, 0.6)
24corner_radius_top_left = 3
25corner_radius_top_right = 3
26corner_radius_bottom_right = 3
27corner_radius_bottom_left = 3
28corner_detail = 5
29
30[node name="Details" type="AcceptDialog"]
31title = "Task Details"
32size = Vector2i(916, 557)
33ok_button_text = "Close"
34script = ExtResource("1_gh7s6")
35
36[node name="VBoxContainer" type="VBoxContainer" parent="."]
37custom_minimum_size = Vector2(900, 500)
38anchors_preset = 15
39anchor_right = 1.0
40anchor_bottom = 1.0
41offset_left = 8.0
42offset_top = 8.0
43offset_right = -8.0
44offset_bottom = -49.0
45grow_horizontal = 2
46grow_vertical = 2
47
48[node name="Category" type="OptionButton" parent="VBoxContainer"]
49unique_name_in_owner = true
50layout_mode = 2
51
52[node name="HSplitContainer" type="HSplitContainer" parent="VBoxContainer"]
53unique_name_in_owner = true
54layout_mode = 2
55size_flags_vertical = 3
56
57[node name="VSplitContainer" type="VSplitContainer" parent="VBoxContainer/HSplitContainer"]
58layout_mode = 2
59size_flags_horizontal = 3
60
61[node name="Description" type="TextEdit" parent="VBoxContainer/HSplitContainer/VSplitContainer"]
62unique_name_in_owner = true
63layout_mode = 2
64size_flags_vertical = 3
65wrap_mode = 1
66
67[node name="StepDetails" type="VBoxContainer" parent="VBoxContainer/HSplitContainer/VSplitContainer"]
68unique_name_in_owner = true
69layout_mode = 2
70size_flags_vertical = 3
71
72[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails"]
73layout_mode = 2
74
75[node name="Label" type="Label" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails/HBoxContainer"]
76layout_mode = 2
77size_flags_horizontal = 3
78text = "Step Details:"
79
80[node name="CloseStepDetails" type="Button" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails/HBoxContainer"]
81unique_name_in_owner = true
82layout_mode = 2
83tooltip_text = "Close"
84icon = SubResource("ImageTexture_op8g0")
85flat = true
86
87[node name="StepEdit" type="TextEdit" parent="VBoxContainer/HSplitContainer/VSplitContainer/StepDetails"]
88unique_name_in_owner = true
89layout_mode = 2
90size_flags_vertical = 3
91wrap_mode = 1
92
93[node name="StepList" type="VBoxContainer" parent="VBoxContainer/HSplitContainer"]
94custom_minimum_size = Vector2(200, 0)
95layout_mode = 2
96
97[node name="CreateStepEdit" type="LineEdit" parent="VBoxContainer/HSplitContainer/StepList"]
98unique_name_in_owner = true
99layout_mode = 2
100placeholder_text = "Create Step"
101right_icon = SubResource("ImageTexture_op8g0")
102
103[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer/HSplitContainer/StepList"]
104unique_name_in_owner = true
105layout_mode = 2
106size_flags_vertical = 3
107theme_override_styles/panel = SubResource("StyleBoxFlat_g2k57")
108
109[node name="StepHolder" parent="VBoxContainer/HSplitContainer/StepList/PanelContainer" instance=ExtResource("2_0ptaf")]
110unique_name_in_owner = true
111layout_mode = 2
112steps_focus_mode = 2
addons/kanban_tasks/view/details/step_entry.gd +0 -135
……@@ -1,135 +0,0 @@
1@tool
2extends HBoxContainer
3
4## Visual representation of a step.
5
6
7signal action_triggered(entry: __StepEntry, action: Actions)
8
9const __EditLabel := preload("../../edit_label/edit_label.gd")
10const __StepData := preload("../../data/step.gd")
11const __Singletons := preload("../../plugin_singleton/singletons.gd")
12const __Shortcuts := preload("../shortcuts.gd")
13const __StepEntry := preload("step_entry.gd")
14
15enum Actions {
16 DELETE,
17 MOVE_UP,
18 MOVE_DOWN,
19 EDIT_HARD, ## Forces the step details to open.
20 EDIT_SOFT, ## Only switches to this step if the details are opened.
21 CLOSE,
22}
23
24@export var context_menu_enabled: bool = true
25
26var done: CheckBox
27var title_label: Label
28var focus_box: StyleBoxFlat
29var context_menu: PopupMenu
30
31var step_data: __StepData
32
33var being_edited := false
34
35
36func _ready() -> void:
37 set_h_size_flags(SIZE_EXPAND_FILL)
38
39 context_menu = PopupMenu.new()
40 context_menu.id_pressed.connect(__action)
41 add_child(context_menu)
42
43 done = CheckBox.new()
44 done.focus_mode = Control.FOCUS_NONE
45 done.toggled.connect(__set_done)
46 add_child(done)
47
48 title_label = Label.new()
49 title_label.set_h_size_flags(SIZE_EXPAND_FILL)
50 title_label.text = step_data.details
51 title_label.max_lines_visible = 1
52 title_label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
53 add_child(title_label)
54
55 focus_box = StyleBoxFlat.new()
56 focus_box.bg_color = Color(1, 1, 1, 0.1)
57
58 notification(NOTIFICATION_THEME_CHANGED)
59
60 step_data.changed.connect(update)
61 update()
62
63
64func _shortcut_input(event: InputEvent) -> void:
65 if not __Shortcuts.should_handle_shortcut(self):
66 return
67 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
68 if not event.is_echo() and event.is_pressed():
69 if shortcuts.rename.matches_event(event):
70 get_viewport().set_input_as_handled()
71 __action(Actions.EDIT_HARD)
72 elif shortcuts.confirm.matches_event(event):
73 get_viewport().set_input_as_handled()
74 done.button_pressed = not done.button_pressed
75
76
77func _gui_input(event: InputEvent) -> void:
78 if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT:
79 accept_event()
80 if context_menu_enabled:
81 __update_context_menu()
82 context_menu.position = get_global_mouse_position()
83 if not get_window().gui_embed_subwindows:
84 context_menu.position += get_window().position
85 context_menu.popup()
86
87 if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed() and event.is_double_click():
88 __action(Actions.EDIT_HARD)
89
90
91func _notification(what) -> void:
92 match(what):
93 NOTIFICATION_DRAW:
94 if has_focus() or being_edited:
95 focus_box.draw(get_canvas_item(), Rect2(Vector2.ZERO, get_rect().size))
96 NOTIFICATION_FOCUS_ENTER:
97 __action(Actions.EDIT_SOFT)
98
99
100func update() -> void:
101 tooltip_text = step_data.details
102 done.set_pressed_no_signal(step_data.done)
103
104 title_label.text = step_data.details
105
106
107func __action(what: Actions) -> void:
108 action_triggered.emit(self, what)
109
110
111func __update_context_menu() -> void:
112 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
113
114 context_menu.clear()
115 context_menu.size = Vector2.ZERO
116
117 if being_edited:
118 context_menu.add_icon_item(get_theme_icon(&"Close", &"EditorIcons"), "Close", Actions.CLOSE)
119 else:
120 context_menu.add_icon_item(get_theme_icon(&"Rename", &"EditorIcons"), "Edit", Actions.EDIT_HARD)
121 context_menu.set_item_shortcut(context_menu.get_item_index(Actions.EDIT_HARD), shortcuts.rename)
122
123 context_menu.add_icon_item(get_theme_icon(&"MoveUp", &"EditorIcons"), "Move Up", Actions.MOVE_UP)
124 context_menu.set_item_disabled(context_menu.get_item_index(Actions.MOVE_UP), get_index() == 0)
125 context_menu.add_icon_item(get_theme_icon(&"MoveDown", &"EditorIcons"), "Move Down", Actions.MOVE_DOWN)
126 context_menu.set_item_disabled(context_menu.get_item_index(Actions.MOVE_DOWN), get_index() == get_parent().get_child_count() - 1)
127
128 context_menu.add_separator()
129
130 context_menu.add_icon_item(get_theme_icon(&"Remove", &"EditorIcons"), "Delete", Actions.DELETE)
131
132
133func __set_done(done: bool) -> void:
134 __action(Actions.CLOSE)
135 step_data.done = done
addons/kanban_tasks/view/details/step_entry.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://jdpqqhwk28us
addons/kanban_tasks/view/details/step_holder.gd +0 -184
……@@ -1,184 +0,0 @@
1@tool
2extends VBoxContainer
3
4
5signal entry_action_triggered(entry: __StepEntry, action: __StepEntry.Actions)
6signal entry_move_requesed(moved_entry: __StepEntry, target_entry: __StepEntry, move_after_target: bool)
7
8const __StepData := preload("../../data/step.gd")
9const __StepEntry := preload("../details/step_entry.gd")
10
11@export var scrollable: bool = true:
12 set(value):
13 if value != scrollable:
14 scrollable = value
15 __update_children_settings()
16
17@export var steps_can_be_removed: bool = true:
18 set(value):
19 if value != steps_can_be_removed:
20 steps_can_be_removed = value
21 __update_children_settings()
22
23@export var steps_can_be_reordered: bool = true:
24 set(value):
25 if value != steps_can_be_reordered:
26 steps_can_be_reordered = value
27 __update_children_settings()
28
29@export var steps_have_context_menu: bool = true:
30 set(value):
31 if value != steps_have_context_menu:
32 steps_have_context_menu = value
33 __update_children_settings()
34
35@export var steps_focus_mode := FocusMode.FOCUS_NONE:
36 set(value):
37 if value != steps_focus_mode:
38 steps_focus_mode = value
39 __update_children_settings()
40
41var __mouse_entered_step_list: bool = false
42var __move_target_entry: __StepEntry = null
43var __move_after_target: bool = false
44
45@onready var __scroll_container: ScrollContainer = %ScrollContainer
46@onready var __remove_separator: HSeparator = %RemoveSeparator
47@onready var __step_list: VBoxContainer = %StepList
48@onready var __remove_area: Button = %RemoveArea
49
50
51func _ready() -> void:
52 __remove_area.icon = get_theme_icon(&"Remove", &"EditorIcons")
53 __step_list.draw.connect(__on_step_list_draw)
54 __step_list.mouse_exited.connect(__on_step_list_mouse_exited)
55 __step_list.mouse_entered.connect(__on_step_list_mouse_entered)
56 __update_children_settings()
57
58
59func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
60 if not steps_can_be_removed and not steps_can_be_reordered:
61 return false
62 if data is __StepEntry:
63 if __remove_area.get_global_rect().has_point(get_global_transform() * at_position):
64 return true
65 __update_move_target(at_position)
66 return (__move_target_entry != null)
67 return false
68
69
70func _get_drag_data(at_position: Vector2) -> Variant:
71 if not steps_can_be_removed and not steps_can_be_reordered:
72 return null
73 for entry in get_step_entries():
74 if entry.get_global_rect().has_point(get_global_transform() * at_position):
75 var preview := Label.new()
76 preview.text = entry.step_data.details
77 set_drag_preview(preview)
78 return entry
79 return null
80
81
82func _drop_data(at_position: Vector2, data: Variant) -> void:
83 if __move_target_entry != null:
84 entry_move_requesed.emit(data, __move_target_entry, __move_after_target)
85 __move_target_entry = null
86 if data is __StepEntry:
87 if __remove_area.get_global_rect().has_point(get_global_transform() * at_position):
88 data.__action(__StepEntry.Actions.DELETE)
89
90
91func add_step(step: __StepData) -> void:
92 var entry = __StepEntry.new()
93 entry.step_data = step
94 entry.show_behind_parent = true
95 entry.size_flags_horizontal = Control.SIZE_EXPAND_FILL
96 __step_list.add_child(entry)
97 entry.size_flags_horizontal = Control.SIZE_EXPAND_FILL
98 entry.action_triggered.connect(__on_entry_action_triggered)
99 entry.context_menu_enabled = steps_have_context_menu
100 entry.focus_mode = steps_focus_mode
101
102
103func clear_steps() -> void:
104 for step in get_step_entries():
105 __step_list.remove_child(step)
106 step.queue_free()
107
108
109func get_step_entries() -> Array[__StepEntry]:
110 var step_entries: Array[__StepEntry] = []
111 if is_instance_valid(__step_list):
112 for child in __step_list.get_children():
113 if child is __StepEntry:
114 step_entries.append(child)
115 return step_entries
116
117
118func __update_children_settings() -> void:
119 if is_instance_valid(__scroll_container):
120 __scroll_container.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO if scrollable else ScrollContainer.SCROLL_MODE_DISABLED
121 if is_instance_valid(__remove_separator):
122 __remove_separator.visible = steps_can_be_removed
123 if is_instance_valid(__remove_area):
124 __remove_area.visible = steps_can_be_removed
125 for entry in get_step_entries():
126 entry.context_menu_enabled = steps_have_context_menu
127 entry.focus_mode = steps_focus_mode
128
129
130func __update_move_target(at_position: Vector2) -> void:
131 var at_global_position := get_global_transform() * at_position
132 # This __mouse_entered_step_list is needed here, as this seemed to be the only reliable solution, as:
133 # 1) something is NOK with transforming at_position to global and compare with step_list.global_rect
134 # 2) cannot decide what is the visible rect of the step_list
135 # 3) _can_drop_data was called even after mouse is outside the list (to the bottom direction)
136 if __mouse_entered_step_list:
137 var closes_entry: __StepEntry = null
138 var smallest_distance: float
139 var position_is_after_closes_entry: bool
140 for e in get_step_entries():
141 var entry_global_rect = e.get_global_rect()
142 var distance := abs(at_global_position.y - entry_global_rect.position.y)
143 if closes_entry == null or distance < smallest_distance:
144 closes_entry = e
145 smallest_distance = distance
146 position_is_after_closes_entry = false
147 distance = abs(at_global_position.y - entry_global_rect.end.y)
148 if closes_entry == null or distance < smallest_distance:
149 closes_entry = e
150 smallest_distance = distance
151 position_is_after_closes_entry = true
152 __move_target_entry = closes_entry
153 __move_after_target = position_is_after_closes_entry
154 else:
155 __move_target_entry = null
156 __step_list.queue_redraw()
157
158
159func __on_step_list_mouse_entered() -> void:
160 __mouse_entered_step_list = true
161
162
163func __on_step_list_mouse_exited() -> void:
164 __mouse_entered_step_list = false
165 __update_move_target(get_local_mouse_position())
166
167
168func __on_step_list_draw() -> void:
169 if __move_target_entry != null:
170 var target_rect := __step_list.get_global_transform().inverse() * __move_target_entry.get_global_rect()
171 var separation = __step_list.get_theme_constant(&"separation")
172 var preview_rect := Rect2(
173 Vector2(0, target_rect.end.y if __move_after_target else target_rect.position.y - separation),
174 Vector2(target_rect.size.x, separation)
175 )
176 if preview_rect.position.y < 0:
177 preview_rect.position.y = 0
178 if preview_rect.end.y > __step_list.size.y:
179 preview_rect.position.y -= (preview_rect.end.y - __step_list.size.y)
180 __step_list.draw_rect(preview_rect, get_theme_color(&"step_move_review_color"))
181
182
183func __on_entry_action_triggered(entry, action) -> void:
184 entry_action_triggered.emit(entry, action)
addons/kanban_tasks/view/details/step_holder.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://bocmwtj7h6y2r
addons/kanban_tasks/view/details/step_holder.tscn +0 -45
……@@ -1,45 +0,0 @@
1[gd_scene load_steps=3 format=3 uid="uid://dwjg5vyxx4g48"]
2
3[ext_resource type="Script" uid="uid://bocmwtj7h6y2r" path="res://addons/kanban_tasks/view/details/step_holder.gd" id="1_exd17"]
4
5[sub_resource type="Theme" id="Theme_1hs0w"]
6StepHolder/base_type = &"VBoxContainer"
7StepHolder/colors/step_move_review_color = Color(0.439216, 0.729412, 0.980392, 0.501961)
8
9[node name="StepHolder" type="VBoxContainer"]
10offset_right = 326.0
11offset_bottom = 500.0
12size_flags_horizontal = 3
13size_flags_vertical = 3
14theme = SubResource("Theme_1hs0w")
15theme_type_variation = &"StepHolder"
16script = ExtResource("1_exd17")
17
18[node name="ScrollContainer" type="ScrollContainer" parent="."]
19unique_name_in_owner = true
20layout_mode = 2
21size_flags_vertical = 3
22horizontal_scroll_mode = 0
23metadata/_edit_use_anchors_ = true
24
25[node name="StepList" type="VBoxContainer" parent="ScrollContainer"]
26unique_name_in_owner = true
27layout_mode = 2
28size_flags_horizontal = 3
29size_flags_vertical = 3
30
31[node name="RemoveSeparator" type="HSeparator" parent="."]
32unique_name_in_owner = true
33layout_mode = 2
34theme_override_constants/separation = 0
35
36[node name="RemoveArea" type="Button" parent="."]
37unique_name_in_owner = true
38custom_minimum_size = Vector2(0, 40)
39layout_mode = 2
40size_flags_vertical = 8
41focus_mode = 0
42mouse_filter = 2
43button_mask = 0
44flat = true
45icon_alignment = 1
addons/kanban_tasks/view/documentation/1.svg +0 -78
……@@ -1,78 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 width="100"
6 height="100"
7 viewBox="0 0 26.458333 26.458333"
8 version="1.1"
9 id="svg5"
10 inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
11 sodipodi:docname="1.svg"
12 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14 xmlns="http://www.w3.org/2000/svg"
15 xmlns:svg="http://www.w3.org/2000/svg">
16 <sodipodi:namedview
17 id="namedview7"
18 pagecolor="#ffffff"
19 bordercolor="#666666"
20 borderopacity="1.0"
21 inkscape:showpageshadow="2"
22 inkscape:pageopacity="0.0"
23 inkscape:pagecheckerboard="0"
24 inkscape:deskcolor="#d1d1d1"
25 inkscape:document-units="px"
26 showgrid="true"
27 inkscape:zoom="2.8284271"
28 inkscape:cx="-102.00015"
29 inkscape:cy="63.462833"
30 inkscape:window-width="2560"
31 inkscape:window-height="1361"
32 inkscape:window-x="-9"
33 inkscape:window-y="-9"
34 inkscape:window-maximized="1"
35 inkscape:current-layer="layer1">
36 <inkscape:grid
37 type="xygrid"
38 id="grid1049" />
39 </sodipodi:namedview>
40 <defs
41 id="defs2" />
42 <g
43 inkscape:label="Layer 1"
44 inkscape:groupmode="layer"
45 id="layer1">
46 <rect
47 style="fill:#575b64;stroke:#575b64;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;fill-opacity:1;stroke-opacity:1"
48 id="rect2968"
49 width="10.583333"
50 height="23.8125"
51 x="1.3229166"
52 y="1.3229166" />
53 <rect
54 style="fill:#626771;stroke:#626771;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;fill-opacity:1;stroke-opacity:1"
55 id="rect2970"
56 width="10.583333"
57 height="23.8125"
58 x="14.552083"
59 y="1.3229166" />
60 <rect
61 style="fill:#179ceb;fill-opacity:1;stroke:#179ceb;stroke-width:1.05836;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
62 id="rect4591"
63 width="15.216529"
64 height="3.9785306"
65 x="2.3476388"
66 y="10.695463"
67 transform="matrix(0.99152896,-0.12988578,0.11857896,0.99294463,0,0)" />
68 <path
69 style="fill:#a4c1d3;fill-opacity:0.60851061;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
70 d="m 16.933333,10.847917 c -0.923401,0.09187 -1.777291,0.468265 -2.910417,1.161686 1.103691,-0.186089 2.064921,-0.191401 3.439584,0.16123 l -0.529167,-1.322916"
71 id="path12560"
72 sodipodi:nodetypes="cccc" />
73 <path
74 id="path12547"
75 style="color:#000000;fill:#d8d8d8;stroke-linejoin:round;-inkscape-stroke:none;fill-opacity:1"
76 d="M 16.930749 10.714591 C 16.914596 10.71489 16.89821 10.718029 16.88269 10.72441 C 16.83287 10.74494 16.800315 10.793523 16.800525 10.8474 L 16.800525 11.90625 C 16.80114 11.99606 16.889346 12.059309 16.974674 12.031307 L 17.202051 11.955859 L 17.252693 12.107788 A 0.1825 0.1825 0 0 0 17.48317 12.223026 A 0.1825 0.1825 0 0 0 17.598409 11.992549 L 17.548283 11.841138 L 17.769458 11.767757 C 17.861908 11.735907 17.889068 11.617968 17.820101 11.548649 L 17.027384 10.753865 C 17.001404 10.727637 16.966286 10.713935 16.930749 10.714591 z " />
77 </g>
78</svg>
addons/kanban_tasks/view/documentation/1.svg.import +0 -37
……@@ -1,37 +0,0 @@
1[remap]
2
3importer="texture"
4type="CompressedTexture2D"
5uid="uid://3edv1ymvukp0"
6path="res://.godot/imported/1.svg-15461a63252c61d47c72a31629894a26.ctex"
7metadata={
8"vram_texture": false
9}
10
11[deps]
12
13source_file="res://addons/kanban_tasks/view/documentation/1.svg"
14dest_files=["res://.godot/imported/1.svg-15461a63252c61d47c72a31629894a26.ctex"]
15
16[params]
17
18compress/mode=0
19compress/high_quality=false
20compress/lossy_quality=0.7
21compress/hdr_compression=1
22compress/normal_map=0
23compress/channel_pack=0
24mipmaps/generate=false
25mipmaps/limit=-1
26roughness/mode=0
27roughness/src_normal=""
28process/fix_alpha_border=true
29process/premult_alpha=false
30process/normal_map_invert_y=false
31process/hdr_as_srgb=false
32process/hdr_clamp_exposure=false
33process/size_limit=0
34detect_3d/compress_to=1
35svg/scale=4.0
36editor/scale_with_editor_scale=false
37editor/convert_colors_with_editor_theme=false
addons/kanban_tasks/view/documentation/2.svg +0 -91
……@@ -1,91 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 width="100"
6 height="100"
7 viewBox="0 0 26.458333 26.458333"
8 version="1.1"
9 id="svg5"
10 inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
11 sodipodi:docname="2.svg"
12 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14 xmlns="http://www.w3.org/2000/svg"
15 xmlns:svg="http://www.w3.org/2000/svg">
16 <sodipodi:namedview
17 id="namedview7"
18 pagecolor="#ffffff"
19 bordercolor="#666666"
20 borderopacity="1.0"
21 inkscape:showpageshadow="2"
22 inkscape:pageopacity="0.0"
23 inkscape:pagecheckerboard="0"
24 inkscape:deskcolor="#d1d1d1"
25 inkscape:document-units="px"
26 showgrid="true"
27 inkscape:zoom="5.6568543"
28 inkscape:cx="-24.483572"
29 inkscape:cy="14.407301"
30 inkscape:window-width="2560"
31 inkscape:window-height="1361"
32 inkscape:window-x="-9"
33 inkscape:window-y="-9"
34 inkscape:window-maximized="1"
35 inkscape:current-layer="layer1">
36 <inkscape:grid
37 type="xygrid"
38 id="grid1049" />
39 </sodipodi:namedview>
40 <defs
41 id="defs2" />
42 <g
43 inkscape:label="Layer 1"
44 inkscape:groupmode="layer"
45 id="layer1">
46 <rect
47 style="fill:#575b64;stroke:#575b64;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;paint-order:markers fill stroke;fill-opacity:1;stroke-opacity:1"
48 id="rect2968"
49 width="10.583333"
50 height="23.8125"
51 x="1.3229166"
52 y="1.3229166" />
53 <rect
54 style="fill:#575b64;fill-opacity:1;stroke:#575b64;stroke-width:1.058;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke;stroke-dasharray:none"
55 id="rect2970"
56 width="10.583333"
57 height="10.583331"
58 x="14.552083"
59 y="14.552083" />
60 <rect
61 style="fill:#575b64;fill-opacity:1;stroke:#575b64;stroke-width:1.05806874;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
62 id="rect16439"
63 width="10.583333"
64 height="10.583333"
65 x="14.552083"
66 y="1.3229166" />
67 <rect
68 style="fill:#179ceb;fill-opacity:1;stroke:#179ceb;stroke-width:1.05807;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
69 id="rect19474"
70 width="9.5251322"
71 height="1.3229165"
72 x="1.8519517"
73 y="2.9104166" />
74 <path
75 style="fill:none;stroke:#106da5;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
76 d="M 2.1166666,3.6049483 H 10.054166"
77 id="path19478" />
78 <path
79 style="fill:#afb7c9;fill-opacity:0.84680849;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
80 d="M 8.4072969,3.5780708 8.1734362,2.7712509 8.7814743,3.4260613 9.1907307,3.0051116 9.295968,3.636536 9.7169177,3.8587037 9.1907307,3.928862 8.968563,4.3147324 8.6996231,3.917169 8.1266639,4.0691784 Z"
81 id="path19618" />
82 <path
83 style="fill:#afb7c9;fill-opacity:0.659574;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
84 d="M 8.2584693,3.4540478 8.0246086,2.6472279 8.6326467,3.3020383 9.0419031,2.8810886 9.1471404,3.512513 9.5680901,3.7346807 9.0419031,3.804839 8.8197354,4.1907094 8.5507955,3.793146 7.9778363,3.9451554 Z"
85 id="path19622" />
86 <path
87 id="path12547"
88 style="color:#000000;fill:#d8d8d8;fill-opacity:1;stroke-linejoin:round;-inkscape-stroke:none"
89 d="m 8.8614739,3.5711183 c -0.016153,2.99e-4 -0.032539,0.0034 -0.048059,0.0098 -0.04982,0.02053 -0.082375,0.06911 -0.082165,0.12299 v 1.05885 c 6.15e-4,0.08981 0.088821,0.153059 0.174149,0.125057 l 0.227377,-0.07545 0.050642,0.151929 a 0.1825,0.1825 0 0 0 0.230477,0.115238 0.1825,0.1825 0 0 0 0.115239,-0.230477 l -0.050126,-0.151411 0.221175,-0.07338 c 0.09245,-0.03185 0.11961,-0.149789 0.050643,-0.219108 l -0.792717,-0.794784 c -0.02598,-0.02623 -0.061098,-0.03993 -0.096635,-0.03927 z" />
90 </g>
91</svg>
addons/kanban_tasks/view/documentation/2.svg.import +0 -37
……@@ -1,37 +0,0 @@
1[remap]
2
3importer="texture"
4type="CompressedTexture2D"
5uid="uid://bbo1gfac2wymg"
6path="res://.godot/imported/2.svg-053c5d997067871191f4000b81461e5d.ctex"
7metadata={
8"vram_texture": false
9}
10
11[deps]
12
13source_file="res://addons/kanban_tasks/view/documentation/2.svg"
14dest_files=["res://.godot/imported/2.svg-053c5d997067871191f4000b81461e5d.ctex"]
15
16[params]
17
18compress/mode=0
19compress/high_quality=false
20compress/lossy_quality=0.7
21compress/hdr_compression=1
22compress/normal_map=0
23compress/channel_pack=0
24mipmaps/generate=false
25mipmaps/limit=-1
26roughness/mode=0
27roughness/src_normal=""
28process/fix_alpha_border=true
29process/premult_alpha=false
30process/normal_map_invert_y=false
31process/hdr_as_srgb=false
32process/hdr_clamp_exposure=false
33process/size_limit=0
34detect_3d/compress_to=1
35svg/scale=4.0
36editor/scale_with_editor_scale=false
37editor/convert_colors_with_editor_theme=false
addons/kanban_tasks/view/documentation/3.svg +0 -100
……@@ -1,100 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 width="100"
6 height="100"
7 viewBox="0 0 26.458333 26.458333"
8 version="1.1"
9 id="svg5"
10 inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
11 sodipodi:docname="3.svg"
12 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14 xmlns="http://www.w3.org/2000/svg"
15 xmlns:svg="http://www.w3.org/2000/svg">
16 <sodipodi:namedview
17 id="namedview7"
18 pagecolor="#ffffff"
19 bordercolor="#666666"
20 borderopacity="1.0"
21 inkscape:showpageshadow="2"
22 inkscape:pageopacity="0.0"
23 inkscape:pagecheckerboard="0"
24 inkscape:deskcolor="#d1d1d1"
25 inkscape:document-units="px"
26 showgrid="true"
27 inkscape:zoom="8"
28 inkscape:cx="36.3125"
29 inkscape:cy="35.1875"
30 inkscape:window-width="2560"
31 inkscape:window-height="1361"
32 inkscape:window-x="-9"
33 inkscape:window-y="-9"
34 inkscape:window-maximized="1"
35 inkscape:current-layer="layer1">
36 <inkscape:grid
37 type="xygrid"
38 id="grid1049" />
39 </sodipodi:namedview>
40 <defs
41 id="defs2" />
42 <g
43 inkscape:label="Layer 1"
44 inkscape:groupmode="layer"
45 id="layer1">
46 <rect
47 style="fill:#575b64;fill-opacity:1;stroke:#575b64;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
48 id="rect2968"
49 width="13.262239"
50 height="18.057812"
51 x="7.2098951"
52 y="4.0348959" />
53 <rect
54 style="fill:#179ceb;fill-opacity:1;stroke:#179ceb;stroke-width:1.05807;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
55 id="rect19474"
56 width="9.5251322"
57 height="1.3229165"
58 x="9.2602844"
59 y="22.489584" />
60 <path
61 style="fill:none;stroke:#106da5;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
62 d="M 9.5250003,23.184123 H 17.462504"
63 id="path19478" />
64 <circle
65 style="fill:#106da5;fill-opacity:1;stroke:none;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
66 id="path23344"
67 cx="18.654341"
68 cy="23.18438"
69 r="0.39464015" />
70 <path
71 style="fill:#afb7c9;fill-opacity:0.846809;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
72 d="m 18.36207,23.157246 -0.233861,-0.80682 0.608038,0.65481 0.409257,-0.42095 0.105237,0.631425 0.42095,0.222168 -0.526187,0.07016 -0.222168,0.385871 -0.26894,-0.397564 -0.572959,0.15201 z"
73 id="path19618" />
74 <path
75 id="path12547"
76 style="color:#000000;fill:#d8d8d8;fill-opacity:1;stroke-linejoin:round;-inkscape-stroke:none"
77 d="m 18.816247,23.150293 c -0.01615,2.99e-4 -0.03254,0.0034 -0.04806,0.0098 -0.04982,0.02053 -0.08237,0.06911 -0.08216,0.122991 v 1.05885 c 6.15e-4,0.08981 0.08882,0.153059 0.174149,0.125057 l 0.227377,-0.07545 0.05064,0.151929 a 0.1825,0.1825 0 0 0 0.230477,0.115238 0.1825,0.1825 0 0 0 0.11524,-0.230477 l -0.05013,-0.151411 0.221175,-0.07338 c 0.09245,-0.03185 0.11961,-0.149789 0.05064,-0.219108 l -0.792711,-0.794785 c -0.02598,-0.02623 -0.0611,-0.03993 -0.09664,-0.03927 z" />
78 <rect
79 style="fill:#646973;fill-opacity:1;stroke:#646973;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
80 id="rect27188"
81 width="12.336198"
82 height="1.1451006"
83 x="7.6729159"
84 y="4.6529698" />
85 <rect
86 style="fill:#646973;fill-opacity:1;stroke:#646973;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
87 id="rect27916"
88 width="8.2020836"
89 height="12.687549"
90 x="7.6729159"
91 y="7.8279738" />
92 <rect
93 style="fill:#646973;fill-opacity:1;stroke:#646973;stroke-width:1.05833;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:markers fill stroke"
94 id="rect27918"
95 width="2.2158854"
96 height="12.687549"
97 x="17.793228"
98 y="7.8279738" />
99 </g>
100</svg>
addons/kanban_tasks/view/documentation/3.svg.import +0 -37
……@@ -1,37 +0,0 @@
1[remap]
2
3importer="texture"
4type="CompressedTexture2D"
5uid="uid://bb8knc6dctfj1"
6path="res://.godot/imported/3.svg-456dac6515246348acd4893e98f4bdc7.ctex"
7metadata={
8"vram_texture": false
9}
10
11[deps]
12
13source_file="res://addons/kanban_tasks/view/documentation/3.svg"
14dest_files=["res://.godot/imported/3.svg-456dac6515246348acd4893e98f4bdc7.ctex"]
15
16[params]
17
18compress/mode=0
19compress/high_quality=false
20compress/lossy_quality=0.7
21compress/hdr_compression=1
22compress/normal_map=0
23compress/channel_pack=0
24mipmaps/generate=false
25mipmaps/limit=-1
26roughness/mode=0
27roughness/src_normal=""
28process/fix_alpha_border=true
29process/premult_alpha=false
30process/normal_map_invert_y=false
31process/hdr_as_srgb=false
32process/hdr_clamp_exposure=false
33process/size_limit=0
34detect_3d/compress_to=1
35svg/scale=4.0
36editor/scale_with_editor_scale=false
37editor/convert_colors_with_editor_theme=false
addons/kanban_tasks/view/documentation/4.svg +0 -64
……@@ -1,64 +0,0 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 width="100"
6 height="100"
7 viewBox="0 0 26.458333 26.458333"
8 version="1.1"
9 id="svg5"
10 inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
11 sodipodi:docname="4.svg"
12 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14 xmlns="http://www.w3.org/2000/svg"
15 xmlns:svg="http://www.w3.org/2000/svg">
16 <sodipodi:namedview
17 id="namedview7"
18 pagecolor="#ffffff"
19 bordercolor="#666666"
20 borderopacity="1.0"
21 inkscape:showpageshadow="2"
22 inkscape:pageopacity="0.0"
23 inkscape:pagecheckerboard="0"
24 inkscape:deskcolor="#d1d1d1"
25 inkscape:document-units="px"
26 showgrid="true"
27 inkscape:zoom="8"
28 inkscape:cx="40.3125"
29 inkscape:cy="44.4375"
30 inkscape:window-width="2560"
31 inkscape:window-height="1361"
32 inkscape:window-x="-9"
33 inkscape:window-y="-9"
34 inkscape:window-maximized="1"
35 inkscape:current-layer="layer1">
36 <inkscape:grid
37 type="xygrid"
38 id="grid1049" />
39 </sodipodi:namedview>
40 <defs
41 id="defs2" />
42 <g
43 inkscape:label="Layer 1"
44 inkscape:groupmode="layer"
45 id="layer1">
46 <path
47 sodipodi:type="star"
48 style="fill:#d0c268;fill-opacity:1;stroke:#d0c268;stroke-width:0.79375;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1;paint-order:markers fill stroke"
49 id="path32362"
50 inkscape:flatsided="false"
51 sodipodi:sides="5"
52 sodipodi:cx="7.8382812"
53 sodipodi:cy="7.9705729"
54 sodipodi:r1="8.6053162"
55 sodipodi:r2="4.6296601"
56 sodipodi:arg1="-1.5707963"
57 sodipodi:arg2="-0.94247777"
58 inkscape:rounded="0.11"
59 inkscape:randomized="0"
60 d="m 7.8382814,-0.63474321 c 0.6126837,1e-8 2.2255746,4.49971601 2.7212456,4.85984251 0.495672,0.3601264 5.273567,0.5035881 5.462896,1.0862849 0.18933,0.5826968 -3.591744,3.5071357 -3.781073,4.0898325 -0.18933,0.5826968 1.15068,5.1710773 0.655009,5.5312033 -0.495672,0.360127 -4.4453943,-2.332187 -5.058078,-2.332187 -0.6126837,0 -4.5624065,2.692313 -5.058078,2.332187 C 2.2845315,14.572293 3.6245423,9.9839133 3.4352127,9.4012165 3.245883,8.8185197 -0.53519047,5.8940806 -0.34586078,5.3113838 -0.1565311,4.728687 4.6213638,4.5852256 5.1170353,4.2250992 5.6127068,3.8649727 7.2255977,-0.63474323 7.8382814,-0.63474321 Z"
61 inkscape:transform-center-y="-0.77805912"
62 transform="translate(5.3908937,6.0569817)" />
63 </g>
64</svg>
addons/kanban_tasks/view/documentation/4.svg.import +0 -37
……@@ -1,37 +0,0 @@
1[remap]
2
3importer="texture"
4type="CompressedTexture2D"
5uid="uid://phpa3kr3tjwu"
6path="res://.godot/imported/4.svg-963688afa901818a04233dc49468c7c7.ctex"
7metadata={
8"vram_texture": false
9}
10
11[deps]
12
13source_file="res://addons/kanban_tasks/view/documentation/4.svg"
14dest_files=["res://.godot/imported/4.svg-963688afa901818a04233dc49468c7c7.ctex"]
15
16[params]
17
18compress/mode=0
19compress/high_quality=false
20compress/lossy_quality=0.7
21compress/hdr_compression=1
22compress/normal_map=0
23compress/channel_pack=0
24mipmaps/generate=false
25mipmaps/limit=-1
26roughness/mode=0
27roughness/src_normal=""
28process/fix_alpha_border=true
29process/premult_alpha=false
30process/normal_map_invert_y=false
31process/hdr_as_srgb=false
32process/hdr_clamp_exposure=false
33process/size_limit=0
34detect_3d/compress_to=1
35svg/scale=4.0
36editor/scale_with_editor_scale=false
37editor/convert_colors_with_editor_theme=false
addons/kanban_tasks/view/documentation/documentation.tscn +0 -138
……@@ -1,138 +0,0 @@
1[gd_scene load_steps=6 format=3 uid="uid://cwfixtyy5lpin"]
2
3[ext_resource type="Texture2D" uid="uid://3edv1ymvukp0" path="res://addons/kanban_tasks/view/documentation/1.svg" id="1_inmy7"]
4[ext_resource type="Texture2D" uid="uid://bbo1gfac2wymg" path="res://addons/kanban_tasks/view/documentation/2.svg" id="2_1g6ul"]
5[ext_resource type="Texture2D" uid="uid://bb8knc6dctfj1" path="res://addons/kanban_tasks/view/documentation/3.svg" id="3_e3hha"]
6[ext_resource type="Texture2D" uid="uid://phpa3kr3tjwu" path="res://addons/kanban_tasks/view/documentation/4.svg" id="4_ic0nh"]
7
8[sub_resource type="LabelSettings" id="LabelSettings_cmnk1"]
9font_size = 20
10
11[node name="AcceptDialog" type="AcceptDialog"]
12title = "Documentation"
13size = Vector2i(1000, 600)
14min_size = Vector2i(1000, 600)
15theme_type_variation = &"EditorSettingsDialog"
16ok_button_text = "Close"
17
18[node name="Help" type="ScrollContainer" parent="."]
19anchors_preset = 15
20anchor_right = 1.0
21anchor_bottom = 1.0
22offset_left = 8.0
23offset_top = 8.0
24offset_right = -8.0
25offset_bottom = -49.0
26grow_horizontal = 2
27grow_vertical = 2
28horizontal_scroll_mode = 0
29
30[node name="VBoxContainer" type="VBoxContainer" parent="Help"]
31layout_mode = 2
32size_flags_horizontal = 3
33theme_override_constants/separation = 10
34
35[node name="PanelContainer1" type="PanelContainer" parent="Help/VBoxContainer"]
36layout_mode = 2
37theme_type_variation = &"Panel"
38
39[node name="HBoxContainer1" type="HBoxContainer" parent="Help/VBoxContainer/PanelContainer1"]
40layout_mode = 2
41theme_override_constants/separation = 50
42alignment = 2
43
44[node name="Control" type="Control" parent="Help/VBoxContainer/PanelContainer1/HBoxContainer1"]
45layout_mode = 2
46
47[node name="Label" type="Label" parent="Help/VBoxContainer/PanelContainer1/HBoxContainer1"]
48layout_mode = 2
49size_flags_horizontal = 3
50text = "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."
51label_settings = SubResource("LabelSettings_cmnk1")
52autowrap_mode = 2
53
54[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/PanelContainer1/HBoxContainer1"]
55custom_minimum_size = Vector2(200, 200)
56layout_mode = 2
57texture = ExtResource("1_inmy7")
58expand_mode = 1
59
60[node name="PanelContainer2" type="PanelContainer" parent="Help/VBoxContainer"]
61layout_mode = 2
62theme_type_variation = &"Panel"
63
64[node name="HBoxContainer2" type="HBoxContainer" parent="Help/VBoxContainer/PanelContainer2"]
65layout_mode = 2
66theme_override_constants/separation = 50
67alignment = 2
68
69[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/PanelContainer2/HBoxContainer2"]
70custom_minimum_size = Vector2(200, 200)
71layout_mode = 2
72texture = ExtResource("2_1g6ul")
73expand_mode = 1
74
75[node name="Label" type="Label" parent="Help/VBoxContainer/PanelContainer2/HBoxContainer2"]
76layout_mode = 2
77size_flags_horizontal = 3
78text = "Boost your productivity by customizing your board!
79Double click stage or task names to change them. Configure categories and change the layout in the settings.
80Find tasks by using the search bar."
81label_settings = SubResource("LabelSettings_cmnk1")
82autowrap_mode = 2
83
84[node name="Control" type="Control" parent="Help/VBoxContainer/PanelContainer2/HBoxContainer2"]
85layout_mode = 2
86
87[node name="PanelContainer3" type="PanelContainer" parent="Help/VBoxContainer"]
88layout_mode = 2
89theme_type_variation = &"Panel"
90
91[node name="HBoxContainer3" type="HBoxContainer" parent="Help/VBoxContainer/PanelContainer3"]
92layout_mode = 2
93theme_override_constants/separation = 50
94alignment = 2
95
96[node name="Control" type="Control" parent="Help/VBoxContainer/PanelContainer3/HBoxContainer3"]
97layout_mode = 2
98
99[node name="Label" type="Label" parent="Help/VBoxContainer/PanelContainer3/HBoxContainer3"]
100layout_mode = 2
101size_flags_horizontal = 3
102text = "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."
103label_settings = SubResource("LabelSettings_cmnk1")
104autowrap_mode = 2
105
106[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/PanelContainer3/HBoxContainer3"]
107custom_minimum_size = Vector2(200, 200)
108layout_mode = 2
109texture = ExtResource("3_e3hha")
110expand_mode = 1
111
112[node name="HBoxContainer4" type="HBoxContainer" parent="Help/VBoxContainer"]
113layout_mode = 2
114theme_override_constants/separation = 50
115alignment = 1
116
117[node name="Control" type="Control" parent="Help/VBoxContainer/HBoxContainer4"]
118layout_mode = 2
119size_flags_horizontal = 3
120
121[node name="HBoxContainer" type="HBoxContainer" parent="Help/VBoxContainer/HBoxContainer4"]
122layout_mode = 2
123
124[node name="TextureRect" type="TextureRect" parent="Help/VBoxContainer/HBoxContainer4/HBoxContainer"]
125custom_minimum_size = Vector2(0, 30)
126layout_mode = 2
127texture = ExtResource("4_ic0nh")
128expand_mode = 3
129
130[node name="LinkButton" type="LinkButton" parent="Help/VBoxContainer/HBoxContainer4/HBoxContainer"]
131layout_mode = 2
132size_flags_vertical = 4
133text = "Leave a star on Github"
134uri = "https://github.com/HolonProduction/godot_kanban_tasks"
135
136[node name="Control2" type="Control" parent="Help/VBoxContainer/HBoxContainer4"]
137layout_mode = 2
138size_flags_horizontal = 3
addons/kanban_tasks/view/edit_context.gd +0 -29
……@@ -1,29 +0,0 @@
1@tool
2extends Node
3
4## Global stuff for the view system.
5
6
7const __Filter := preload("filter.gd")
8const __SettingData := preload("../data/settings.gd")
9
10signal filter_changed()
11signal save_board()
12signal create_board()
13signal reload_board()
14
15## The currently active filter.
16var filter: __Filter = null:
17 set(value):
18 filter = value
19 filter_changed.emit()
20
21## The undo redo for task operations.
22var undo_redo := UndoRedo.new()
23
24## uuid of the object that should have focus. This is used to persist focus
25## when updating some views.
26var focus: String = ""
27
28## Settings that are not tied to the board.
29var settings := __SettingData.new()
addons/kanban_tasks/view/edit_context.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://dtjsuc0ac7p5b
addons/kanban_tasks/view/filter.gd +0 -14
……@@ -1,14 +0,0 @@
1@tool
2extends RefCounted
3
4## A filter configuration for searching tasks.
5
6
7var text: String
8## Whether to search in descriptions.
9var advanced: bool
10
11
12func _init(p_text: String = "", p_advanced: bool = false) -> void:
13 text = p_text
14 advanced = p_advanced
addons/kanban_tasks/view/filter.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://xepb1yyi04s1
addons/kanban_tasks/view/settings/categories/categories.gd +0 -53
……@@ -1,53 +0,0 @@
1@tool
2extends VBoxContainer
3
4
5const __BoardData := preload("../../../data/board.gd")
6const __CategoryEntry := preload("../../settings/categories/category_entry.gd")
7const __CategoryData := preload("../../../data/category.gd")
8const __EditLabel := preload("../../../edit_label/edit_label.gd")
9
10var board_data: __BoardData
11
12var randomizer := RandomNumberGenerator.new()
13
14@onready var category_holder: VBoxContainer = %CategoryHolder
15@onready var scroll_container: ScrollContainer = %ScrollContainer
16@onready var add_category_button: Button = %Add
17
18
19func _ready() -> void:
20 notification(NOTIFICATION_THEME_CHANGED)
21 randomizer.randomize()
22 add_category_button.pressed.connect(__add_category)
23
24
25func _notification(what: int) -> void:
26 match what:
27 NOTIFICATION_THEME_CHANGED:
28 if is_instance_valid(add_category_button):
29 add_category_button.icon = get_theme_icon(&"Add", &"EditorIcons")
30
31
32func update() -> void:
33 for category in category_holder.get_children():
34 category.queue_free()
35
36 for uuid in board_data.get_categories():
37 var entry := __CategoryEntry.new()
38 entry.board_data = board_data
39 entry.data_uuid = uuid
40
41 category_holder.add_child(entry)
42
43
44func __add_category() -> void:
45 var color = Color.from_hsv(randomizer.randf(), randomizer.randf_range(0.8, 1.0), randomizer.randf_range(0.7, 1.0))
46 var data = __CategoryData.new("New category", color)
47 var uuid = board_data.add_category(data)
48 update()
49 for i in category_holder.get_children():
50 if i.data_uuid == uuid:
51 await get_tree().create_timer(0.0).timeout
52 i.grab_focus()
53 i.show_edit(__EditLabel.INTENTION.REPLACE)
addons/kanban_tasks/view/settings/categories/categories.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://cnxvnrtd3anj4
addons/kanban_tasks/view/settings/categories/categories.tscn +0 -65
……@@ -1,65 +0,0 @@
1[gd_scene load_steps=5 format=3 uid="uid://b2likgss81t0s"]
2
3[ext_resource type="Script" uid="uid://cnxvnrtd3anj4" path="res://addons/kanban_tasks/view/settings/categories/categories.gd" id="1_n36ev"]
4
5[sub_resource type="Image" id="Image_jriv5"]
6data = {
7"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
8"format": "RGBA8",
9"height": 16,
10"mipmaps": false,
11"width": 16
12}
13
14[sub_resource type="ImageTexture" id="ImageTexture_w6wkd"]
15image = SubResource("Image_jriv5")
16
17[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_gsyou"]
18content_margin_left = 4.0
19content_margin_top = 4.0
20content_margin_right = 4.0
21content_margin_bottom = 5.0
22bg_color = Color(0.1, 0.1, 0.1, 0.6)
23corner_radius_top_left = 3
24corner_radius_top_right = 3
25corner_radius_bottom_right = 3
26corner_radius_bottom_left = 3
27corner_detail = 5
28
29[node name="Categories" type="VBoxContainer"]
30anchors_preset = 15
31anchor_right = 1.0
32anchor_bottom = 1.0
33grow_horizontal = 2
34grow_vertical = 2
35size_flags_horizontal = 3
36script = ExtResource("1_n36ev")
37
38[node name="Header" type="HBoxContainer" parent="."]
39layout_mode = 2
40
41[node name="\'Available Categories\'" type="Label" parent="Header"]
42layout_mode = 2
43size_flags_horizontal = 3
44text = "Available Categories:"
45
46[node name="Add" type="Button" parent="Header"]
47unique_name_in_owner = true
48layout_mode = 2
49icon = SubResource("ImageTexture_w6wkd")
50
51[node name="PanelContainer" type="PanelContainer" parent="."]
52layout_mode = 2
53size_flags_vertical = 3
54theme_override_styles/panel = SubResource("StyleBoxFlat_gsyou")
55
56[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
57unique_name_in_owner = true
58layout_mode = 2
59size_flags_vertical = 3
60follow_focus = true
61
62[node name="CategoryHolder" type="VBoxContainer" parent="PanelContainer/ScrollContainer"]
63unique_name_in_owner = true
64layout_mode = 2
65size_flags_horizontal = 3
addons/kanban_tasks/view/settings/categories/category_entry.gd +0 -94
……@@ -1,94 +0,0 @@
1@tool
2extends HBoxContainer
3
4## Visual representation of a category.
5
6
7const __EditLabel := preload("../../../edit_label/edit_label.gd")
8const __BoardData := preload("../../../data/board.gd")
9const __Singletons := preload("../../../plugin_singleton/singletons.gd")
10const __Shortcuts := preload("../../shortcuts.gd")
11
12var title: __EditLabel
13var delete: Button
14var color_picker: ColorPickerButton
15var focus_box: StyleBoxFlat
16
17var board_data: __BoardData
18var data_uuid: String
19
20
21func _ready() -> void:
22 set_h_size_flags(SIZE_EXPAND_FILL)
23 focus_mode = FOCUS_ALL
24 title = __EditLabel.new()
25 title.set_h_size_flags(SIZE_EXPAND_FILL)
26 title.text = board_data.get_category(data_uuid).title
27 title.text_changed.connect(__on_title_changed)
28 add_child(title)
29
30 color_picker = ColorPickerButton.new()
31 color_picker.custom_minimum_size.x = 100
32 color_picker.edit_alpha = false
33 color_picker.color = board_data.get_category(data_uuid).color
34 color_picker.focus_mode = Control.FOCUS_NONE
35 color_picker.flat = true
36 color_picker.popup_closed.connect(__on_color_changed)
37 add_child(color_picker)
38
39 delete = Button.new()
40 delete.focus_mode = FOCUS_NONE
41 delete.flat = true
42 delete.disabled = board_data.get_category_count() <= 1
43 delete.pressed.connect(__on_delete)
44 add_child(delete)
45
46 focus_box = StyleBoxFlat.new()
47 focus_box.bg_color = Color(1, 1, 1, 0.1)
48
49 notification(NOTIFICATION_THEME_CHANGED)
50
51
52func _shortcut_input(event: InputEvent) -> void:
53 if not __Shortcuts.should_handle_shortcut(self):
54 return
55 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
56 if not event.is_echo() and event.is_pressed():
57 if shortcuts.rename.matches_event(event):
58 get_viewport().set_input_as_handled()
59 title.show_edit()
60
61
62func _notification(what) -> void:
63 match(what):
64 NOTIFICATION_THEME_CHANGED:
65 if is_instance_valid(delete):
66 delete.icon = get_theme_icon(&"Remove", &"EditorIcons")
67 NOTIFICATION_DRAW:
68 if has_focus():
69 focus_box.draw(get_canvas_item(), Rect2(Vector2.ZERO, get_rect().size))
70
71
72func show_edit(intention: int = title.default_intention) -> void:
73 title.show_edit(intention)
74
75
76func __on_title_changed(new: String) -> void:
77 board_data.get_category(data_uuid).title = new
78
79
80func __on_color_changed() -> void:
81 board_data.get_category(data_uuid).color = color_picker.color
82 # Hack to get the tasks to update their color.
83 board_data.layout.changed.emit()
84
85
86func __on_delete() -> void:
87 board_data.remove_category(data_uuid)
88
89 var fallback_to = board_data.get_categories()[0]
90 for uuid in board_data.get_tasks():
91 if board_data.get_task(uuid).category == data_uuid:
92 board_data.get_task(uuid).category = fallback_to
93
94 get_parent().get_owner().update()
addons/kanban_tasks/view/settings/categories/category_entry.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://c5flkwp0lqd4
addons/kanban_tasks/view/settings/general/general.gd +0 -141
……@@ -1,141 +0,0 @@
1@tool
2extends VBoxContainer
3
4
5const __Singletons := preload("../../../plugin_singleton/singletons.gd")
6const __EditContext := preload("../../edit_context.gd")
7const __SettingData := preload("../../../data/settings.gd")
8
9var data: __SettingData = null
10
11var file_dialog_open_option: CheckBox
12var file_dialog_save_option: CheckBox
13var file_dialog_create_option: CheckBox
14var file_dialog_option_button_group: ButtonGroup
15
16@onready var show_description_preview: CheckBox = %ShowDescriptionPreview
17@onready var show_steps_preview: CheckBox = %ShowStepsPreview
18@onready var show_category_on_board: CheckBox = %ShowCategoriesOnBoard
19@onready var edit_step_details_exclusively: CheckBox = %EditStepDetailsExclusively
20@onready var max_displayed_lines_in_description: SpinBox = %MaxDisplayedLinesInDescription
21@onready var description_on_board: OptionButton = %DescriptionOnBoard
22# Keep IDs of the items of StepsOnBoard in sync with the values of setting.gd/StepsOnBoard
23@onready var steps_on_board: OptionButton = %StepsOnBoard
24@onready var max_steps_on_board: SpinBox = %MaxStepsOnBoard
25@onready var data_file_path_label: Control = %DataFilePathLabel
26@onready var data_file_path_container: Control = %DataFilePathContainer
27@onready var data_file_path: LineEdit = %DataFilePath
28@onready var data_file_path_button: Button = %DataFilePathButton
29@onready var file_dialog: FileDialog = %FileDialog
30
31
32func _ready() -> void:
33 await get_tree().create_timer(0.0).timeout
34 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
35 data = ctx.settings
36 data.changed.connect(update)
37 update()
38
39 show_description_preview.toggled.connect(func(x): __apply_changes())
40 show_steps_preview.toggled.connect(func(x): __apply_changes())
41 show_category_on_board.toggled.connect(func(x): __apply_changes())
42 edit_step_details_exclusively.toggled.connect(func(x): __apply_changes())
43 max_displayed_lines_in_description.value_changed.connect(func(x): __apply_changes())
44 description_on_board.item_selected.connect(func(x): __apply_changes())
45 steps_on_board.item_selected.connect(func(x): __apply_changes())
46 max_steps_on_board.value_changed.connect(func(x): __apply_changes())
47 if not Engine.is_editor_hint():
48 data_file_path_label.visible = false
49 data_file_path_container.visible = false
50 data_file_path_button.pressed.connect(__open_data_file_path_dialog)
51
52 file_dialog_open_option = CheckBox.new()
53 file_dialog_open_option.text = "Open board from existing file"
54 file_dialog.get_vbox().add_child(file_dialog_open_option)
55 file_dialog_save_option = CheckBox.new()
56 file_dialog_save_option.text = "Save current board to file"
57 file_dialog.get_vbox().add_child(file_dialog_save_option)
58 file_dialog_create_option = CheckBox.new()
59 file_dialog_create_option.text = "Create new board in file"
60 file_dialog.get_vbox().add_child(file_dialog_create_option)
61 file_dialog_option_button_group = ButtonGroup.new()
62 file_dialog_open_option.button_group = file_dialog_option_button_group
63 file_dialog_save_option.button_group = file_dialog_option_button_group
64 file_dialog_create_option.button_group = file_dialog_option_button_group
65 file_dialog_option_button_group.pressed.connect(func (button): __update_file_dialog())
66 file_dialog.get_line_edit().text_changed.connect(func (new_text): __update_file_dialog())
67 file_dialog_open_option.button_pressed = true
68
69 file_dialog.file_selected.connect(__update_editor_data_file)
70
71
72func update() -> void:
73 show_description_preview.button_pressed = data.show_description_preview
74 show_steps_preview.button_pressed = data.show_steps_preview
75 show_category_on_board.button_pressed = data.show_category_on_board
76 edit_step_details_exclusively.button_pressed = data.edit_step_details_exclusively
77 max_displayed_lines_in_description.value = data.max_displayed_lines_in_description
78 max_steps_on_board.value = data.max_steps_on_board
79
80 description_on_board.select(description_on_board.get_item_index(data.description_on_board))
81 steps_on_board.select(steps_on_board.get_item_index(data.steps_on_board))
82
83 data_file_path.text = data.editor_data_file_path
84
85
86func __open_data_file_path_dialog() -> void:
87 file_dialog_open_option.set_pressed_no_signal(true)
88 file_dialog_save_option.set_pressed_no_signal(false)
89 file_dialog_create_option.set_pressed_no_signal(false)
90 file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
91 file_dialog.clear_filters()
92 file_dialog.add_filter("*.kanban, *.json", "Kanban Board")
93 file_dialog.popup_centered(file_dialog.size)
94
95
96func __update_file_dialog() -> void:
97 if file_dialog_save_option.button_pressed:
98 file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
99 file_dialog.title = file_dialog_save_option.text
100 file_dialog.ok_button_text = "Save"
101 elif file_dialog_create_option.button_pressed:
102 file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
103 file_dialog.title = file_dialog_create_option.text
104 file_dialog.ok_button_text = "Create"
105 else:
106 file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
107 file_dialog.title = file_dialog_open_option.text
108 file_dialog.ok_button_text = "Open"
109
110
111func __update_editor_data_file(path: String) -> void:
112 data_file_path.text = path
113 __apply_changes()
114
115 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
116 if file_dialog_save_option.button_pressed:
117 ctx.save_board.emit()
118 elif file_dialog_create_option.button_pressed:
119 ctx.create_board.emit()
120 else:
121 ctx.reload_board.emit()
122
123
124func __apply_changes() -> void:
125 if data.changed.is_connected(update):
126 data.changed.disconnect(update)
127
128 data.__emit_changed = false
129 data.show_description_preview = show_description_preview.button_pressed
130 data.show_steps_preview = show_steps_preview.button_pressed
131 data.show_category_on_board = show_category_on_board.button_pressed
132 data.edit_step_details_exclusively = edit_step_details_exclusively.button_pressed
133 data.max_displayed_lines_in_description = max_displayed_lines_in_description.value
134 data.description_on_board = description_on_board.get_selected_id()
135 data.steps_on_board = steps_on_board.get_selected_id()
136 data.max_steps_on_board = max_steps_on_board.value
137 data.editor_data_file_path = data_file_path.text
138 data.__emit_changed = true
139 data.__notify_changed()
140
141 data.changed.connect(update)
addons/kanban_tasks/view/settings/general/general.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://dxelpuascaqc2
addons/kanban_tasks/view/settings/general/general.tscn +0 -172
……@@ -1,172 +0,0 @@
1[gd_scene load_steps=3 format=3 uid="uid://due07vdflx4o"]
2
3[ext_resource type="Script" uid="uid://dxelpuascaqc2" path="res://addons/kanban_tasks/view/settings/general/general.gd" id="1_8tblh"]
4
5[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7cqkc"]
6content_margin_left = 4.0
7content_margin_top = 4.0
8content_margin_right = 4.0
9content_margin_bottom = 5.0
10bg_color = Color(0.1, 0.1, 0.1, 0.6)
11corner_radius_top_left = 3
12corner_radius_top_right = 3
13corner_radius_bottom_right = 3
14corner_radius_bottom_left = 3
15corner_detail = 5
16
17[node name="General" type="VBoxContainer"]
18anchors_preset = 15
19anchor_right = 1.0
20anchor_bottom = 1.0
21grow_horizontal = 2
22grow_vertical = 2
23script = ExtResource("1_8tblh")
24
25[node name="PanelContainer" type="PanelContainer" parent="."]
26layout_mode = 2
27size_flags_vertical = 3
28theme_override_styles/panel = SubResource("StyleBoxFlat_7cqkc")
29
30[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
31layout_mode = 2
32
33[node name="GridContainer" type="GridContainer" parent="PanelContainer/ScrollContainer"]
34layout_mode = 2
35size_flags_horizontal = 3
36size_flags_vertical = 3
37columns = 2
38
39[node name="ShowDescriptionPreviewLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
40layout_mode = 2
41size_flags_horizontal = 3
42text = "Show description preview"
43
44[node name="ShowDescriptionPreview" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
45unique_name_in_owner = true
46layout_mode = 2
47size_flags_horizontal = 3
48focus_mode = 0
49button_pressed = true
50text = "On"
51
52[node name="ShowStepsPreviewLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
53layout_mode = 2
54size_flags_horizontal = 3
55text = "Show steps preview"
56
57[node name="ShowStepsPreview" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
58unique_name_in_owner = true
59layout_mode = 2
60size_flags_horizontal = 3
61focus_mode = 0
62button_pressed = true
63text = "On"
64
65[node name="ShowCategoriesOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
66layout_mode = 2
67size_flags_horizontal = 3
68text = "Show categories on board"
69
70[node name="ShowCategoriesOnBoard" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
71unique_name_in_owner = true
72layout_mode = 2
73size_flags_horizontal = 3
74focus_mode = 0
75button_pressed = true
76text = "On"
77
78[node name="EditStepDetailsExclusivelyLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
79layout_mode = 2
80size_flags_horizontal = 3
81text = "Edit step details in fullscreen"
82
83[node name="EditStepDetailsExclusively" type="CheckBox" parent="PanelContainer/ScrollContainer/GridContainer"]
84unique_name_in_owner = true
85layout_mode = 2
86size_flags_horizontal = 3
87focus_mode = 0
88text = "On"
89
90[node name="DescriptionOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
91layout_mode = 2
92text = "Description on board"
93
94[node name="DescriptionOnBoard" type="OptionButton" parent="PanelContainer/ScrollContainer/GridContainer"]
95unique_name_in_owner = true
96layout_mode = 2
97item_count = 3
98selected = 0
99popup/item_0/text = "Full description"
100popup/item_0/id = 0
101popup/item_1/text = "First line of description"
102popup/item_1/id = 1
103popup/item_2/text = "Until first blank line of description"
104popup/item_2/id = 2
105
106[node name="MaxDisplayedLinesInDescriptionLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
107layout_mode = 2
108size_flags_horizontal = 3
109text = "Maximum displayed lines in description"
110
111[node name="MaxDisplayedLinesInDescription" type="SpinBox" parent="PanelContainer/ScrollContainer/GridContainer"]
112unique_name_in_owner = true
113layout_mode = 2
114size_flags_horizontal = 3
115value = 2.0
116allow_greater = true
117
118[node name="StepsOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
119layout_mode = 2
120text = "Steps on board"
121
122[node name="StepsOnBoard" type="OptionButton" parent="PanelContainer/ScrollContainer/GridContainer"]
123unique_name_in_owner = true
124layout_mode = 2
125item_count = 3
126selected = 0
127popup/item_0/text = "Only open steps"
128popup/item_0/id = 0
129popup/item_1/text = "All, but open first"
130popup/item_1/id = 1
131popup/item_2/text = "All in original order"
132popup/item_2/id = 2
133
134[node name="MaxStepsOnBoardLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
135layout_mode = 2
136size_flags_horizontal = 3
137text = "Maximum steps on board"
138
139[node name="MaxStepsOnBoard" type="SpinBox" parent="PanelContainer/ScrollContainer/GridContainer"]
140unique_name_in_owner = true
141layout_mode = 2
142size_flags_horizontal = 3
143value = 2.0
144allow_greater = true
145
146[node name="DataFilePathLabel" type="Label" parent="PanelContainer/ScrollContainer/GridContainer"]
147unique_name_in_owner = true
148layout_mode = 2
149text = "Data file path"
150
151[node name="DataFilePathContainer" type="HBoxContainer" parent="PanelContainer/ScrollContainer/GridContainer"]
152unique_name_in_owner = true
153layout_mode = 2
154
155[node name="DataFilePath" type="LineEdit" parent="PanelContainer/ScrollContainer/GridContainer/DataFilePathContainer"]
156unique_name_in_owner = true
157layout_mode = 2
158size_flags_horizontal = 3
159text = "res://kanban_tasks_data.kanban"
160editable = false
161
162[node name="DataFilePathButton" type="Button" parent="PanelContainer/ScrollContainer/GridContainer/DataFilePathContainer"]
163unique_name_in_owner = true
164layout_mode = 2
165text = " ... "
166
167[node name="FileDialog" type="FileDialog" parent="."]
168unique_name_in_owner = true
169title = "Open board from existing file"
170size = Vector2i(800, 600)
171ok_button_text = "Save"
172mode_overrides_title = false
addons/kanban_tasks/view/settings/settings.gd +0 -28
……@@ -1,28 +0,0 @@
1@tool
2extends AcceptDialog
3
4
5const __BoardData := preload("../../data/board.gd")
6const __CategoriesScene := preload("../settings/categories/categories.tscn")
7const __CategoriesScript := preload("../settings/categories/categories.gd")
8
9@onready var category_settings: __CategoriesScript = %Categories
10@onready var stage_settings = %Stages
11
12var board_data: __BoardData
13
14
15func _ready() -> void:
16 # Wait for board to set board_data.
17 await get_tree().create_timer(0.0).timeout
18
19 category_settings.board_data = board_data
20 stage_settings.board_data = board_data
21 about_to_popup.connect(stage_settings.update)
22 about_to_popup.connect(category_settings.update)
23
24
25# Workaround for godotengine/godot#70451
26func popup_centered_ratio_no_fullscreen(ratio: float = 0.8) -> void:
27 var viewport: Viewport = get_parent().get_viewport()
28 popup(Rect2i(Vector2(viewport.position) + viewport.size / 2.0 - viewport.size * ratio / 2.0, viewport.size * ratio))
addons/kanban_tasks/view/settings/settings.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://ecq3h0pvi24h
addons/kanban_tasks/view/settings/settings.tscn +0 -39
……@@ -1,39 +0,0 @@
1[gd_scene load_steps=5 format=3 uid="uid://dh1yunmhipirg"]
2
3[ext_resource type="Script" uid="uid://ecq3h0pvi24h" path="res://addons/kanban_tasks/view/settings/settings.gd" id="1_4eaw3"]
4[ext_resource type="PackedScene" uid="uid://due07vdflx4o" path="res://addons/kanban_tasks/view/settings/general/general.tscn" id="1_dk7pg"]
5[ext_resource type="PackedScene" uid="uid://b2likgss81t0s" path="res://addons/kanban_tasks/view/settings/categories/categories.tscn" id="3_iycb0"]
6[ext_resource type="PackedScene" uid="uid://dapkpnkm8sow8" path="res://addons/kanban_tasks/view/settings/stages/stages.tscn" id="4_okolg"]
7
8[node name="Settings" type="AcceptDialog"]
9title = "Settings"
10size = Vector2i(800, 400)
11visible = true
12theme_type_variation = &"EditorSettingsDialog"
13script = ExtResource("1_4eaw3")
14
15[node name="Settings" type="TabContainer" parent="."]
16custom_minimum_size = Vector2(600, 300)
17anchors_preset = 15
18anchor_right = 1.0
19anchor_bottom = 1.0
20offset_left = 8.0
21offset_top = 8.0
22offset_right = -8.0
23offset_bottom = -49.0
24grow_horizontal = 2
25grow_vertical = 2
26
27[node name="General" parent="Settings" instance=ExtResource("1_dk7pg")]
28unique_name_in_owner = true
29layout_mode = 2
30
31[node name="Categories" parent="Settings" instance=ExtResource("3_iycb0")]
32unique_name_in_owner = true
33visible = false
34layout_mode = 2
35
36[node name="Stages" parent="Settings" instance=ExtResource("4_okolg")]
37unique_name_in_owner = true
38visible = false
39layout_mode = 2
addons/kanban_tasks/view/settings/stages/stages.gd +0 -199
……@@ -1,199 +0,0 @@
1@tool
2extends VBoxContainer
3
4
5const __Singletons := preload("../../../plugin_singleton/singletons.gd")
6const __EditContext := preload("../../edit_context.gd")
7const __BoardData = preload("../../../data/board.gd")
8const __StageData = preload("../../../data/stage.gd")
9
10var board_data: __BoardData
11
12var stylebox_n: StyleBoxFlat
13var stylebox_hp: StyleBoxFlat
14
15@onready var column_holder: HBoxContainer = %ColumnHolder
16@onready var column_add: Button = %AddColumn
17@onready var warning_sign: Button = %WarningSign
18@onready var warn_about_empty_deletion: CheckBox = %WarnAboutEmptyDeletion
19@onready var confirm_not_empty: ConfirmationDialog = %ConfirmNotEmpty
20@onready var confirm_empty: ConfirmationDialog = %ConfirmEmpty
21@onready var task_destination: OptionButton = %TaskDestination
22
23
24func _ready() -> void:
25 column_add.focus_mode = Control.FOCUS_NONE
26 column_add.pressed.connect(__on_add_stage.bind(-1))
27
28 var center_container := CenterContainer.new()
29 center_container.set_anchors_preset(Control.PRESET_FULL_RECT)
30 center_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
31 column_add.add_child(center_container)
32
33 var plus := TextureRect.new()
34 plus.mouse_filter = Control.MOUSE_FILTER_IGNORE
35 center_container.add_child(plus)
36
37 notification(NOTIFICATION_THEME_CHANGED)
38
39 await get_tree().create_timer(0.0).timeout
40 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
41 ctx.settings.changed.connect(__settings_changed)
42
43 warn_about_empty_deletion.toggled.connect(__apply_settings_changes)
44
45
46func _notification(what) -> void:
47 match(what):
48 NOTIFICATION_THEME_CHANGED:
49 stylebox_n = get_theme_stylebox(&"normal", &"Button").duplicate()
50 stylebox_n.set_border_width_all(1)
51 stylebox_n.border_color = Color8(32, 32, 32, 255)
52
53 stylebox_hp = get_theme_stylebox(&"read_only", &"LineEdit").duplicate()
54 stylebox_hp.set_border_width_all(1)
55 stylebox_hp.border_color = Color8(32, 32, 32, 128)
56
57 if is_instance_valid(column_add):
58 column_add.get_child(0).get_child(0).texture = get_theme_icon(&"Add", &"EditorIcons")
59 column_add.add_theme_stylebox_override(&"normal", stylebox_n)
60 column_add.add_theme_stylebox_override(&"hover", stylebox_hp)
61 column_add.add_theme_stylebox_override(&"pressed", stylebox_hp)
62 if is_instance_valid(warning_sign):
63 warning_sign.icon = get_theme_icon(&"NodeWarning", &"EditorIcons")
64
65
66func update() -> void:
67 if not board_data.layout.changed.is_connected(update):
68 board_data.layout.changed.connect(update)
69
70 var too_high = false
71 for column in board_data.layout.columns:
72 if len(column) > 3:
73 too_high = true
74 warning_sign.visible = too_high or len(board_data.layout.columns) > 4
75
76 for child in column_holder.get_children():
77 child.queue_free()
78
79 var index = 0
80 for column in board_data.layout.columns:
81 var column_entry := VBoxContainer.new()
82 column_entry.add_theme_constant_override(&"separation", 5)
83 column_holder.add_child(column_entry)
84
85 for stage in column:
86 var stage_entry := Button.new()
87 stage_entry.tooltip_text = board_data.get_stage(stage).title
88 stage_entry.focus_mode = Control.FOCUS_NONE
89 stage_entry.set_v_size_flags(SIZE_EXPAND_FILL)
90 stage_entry.custom_minimum_size = Vector2i(70, 50)
91 stage_entry.add_theme_stylebox_override(&"normal", stylebox_n)
92 stage_entry.add_theme_stylebox_override(&"hover", stylebox_hp)
93 stage_entry.add_theme_stylebox_override(&"pressed", stylebox_hp)
94 stage_entry.add_theme_stylebox_override(&"disabled", stylebox_hp)
95 stage_entry.pressed.connect(__on_remove_stage.bind(stage))
96 stage_entry.disabled = len(board_data.layout.columns) <= 1 and len(board_data.layout.columns[0]) <= 1
97 column_entry.add_child(stage_entry)
98
99 var center_container := CenterContainer.new()
100 center_container.set_anchors_preset(Control.PRESET_FULL_RECT)
101 center_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
102 stage_entry.add_child(center_container)
103
104 var remove := TextureRect.new()
105 remove.mouse_filter = Control.MOUSE_FILTER_IGNORE
106 remove.texture = get_theme_icon(&"Remove", &"EditorIcons")
107 center_container.add_child(remove)
108
109 var add = Button.new()
110 add.custom_minimum_size = Vector2i(70, 40)
111 add.focus_mode = Control.FOCUS_NONE
112 add.pressed.connect(__on_add_stage.bind(index))
113 add.add_theme_stylebox_override(&"normal", stylebox_n)
114 add.add_theme_stylebox_override(&"hover", stylebox_hp)
115 add.add_theme_stylebox_override(&"pressed", stylebox_hp)
116 column_entry.add_child(add)
117
118 var center_container = CenterContainer.new()
119 center_container.set_anchors_preset(Control.PRESET_FULL_RECT)
120 center_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
121 add.add_child(center_container)
122
123 var plus := TextureRect.new()
124 plus.mouse_filter = Control.MOUSE_FILTER_IGNORE
125 center_container.add_child(plus)
126 plus.texture = get_theme_icon(&"Add", &"EditorIcons")
127
128 index += 1
129
130
131func __on_add_stage(column: int) -> void:
132 var data = __StageData.new("New Stage")
133 var uuid = board_data.add_stage(data, true)
134
135 var columns = board_data.layout.columns
136 if column < len(board_data.layout.columns) and column >= 0:
137 columns[column].append(uuid)
138 else:
139 columns.append(PackedStringArray([uuid]))
140 board_data.layout.columns = columns
141
142
143func __on_remove_stage(uuid: String) -> void:
144 if len(board_data.get_stage(uuid).tasks) == 0:
145 if warn_about_empty_deletion.button_pressed:
146 if confirm_empty.confirmed.is_connected(__remove_stage):
147 confirm_empty.confirmed.disconnect(__remove_stage)
148 confirm_empty.confirmed.connect(__remove_stage.bind(uuid))
149 confirm_empty.popup_centered()
150 else:
151 __remove_stage(uuid)
152 else:
153 __update_task_destination(uuid)
154 if confirm_not_empty.confirmed.is_connected(__remove_stage):
155 confirm_not_empty.confirmed.disconnect(__remove_stage)
156 confirm_not_empty.confirmed.connect(__remove_stage.bind(uuid))
157 confirm_not_empty.popup_centered()
158
159
160func __update_task_destination(uuid: String) -> void:
161 task_destination.clear()
162 for stage in board_data.get_stages():
163 if stage != uuid:
164 task_destination.add_item(board_data.get_stage(stage).title)
165 task_destination.set_item_metadata(-1, stage)
166
167
168func __remove_stage(uuid: String) -> void:
169 if len(board_data.get_stage(uuid).tasks) > 0:
170 var old_tasks = board_data.get_stage(uuid).tasks
171 var new_tasks = board_data.get_stage(task_destination.get_selected_metadata()).tasks
172 for task in old_tasks.duplicate():
173 old_tasks.erase(task)
174 new_tasks.append(task)
175 board_data.get_stage(uuid).tasks = old_tasks
176 board_data.get_stage(task_destination.get_selected_metadata()).tasks = new_tasks
177
178 board_data.remove_stage(uuid, true)
179
180 var columns = board_data.layout.columns
181 for column in columns.duplicate():
182 if uuid in column:
183 column.remove_at(column.find(uuid))
184 if len(column) == 0:
185 columns.erase(column)
186
187 board_data.layout.columns = columns
188
189
190func __settings_changed() -> void:
191 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
192 warn_about_empty_deletion.button_pressed = ctx.settings.warn_about_empty_deletion
193
194
195func __apply_settings_changes(warn: bool) -> void:
196 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
197 ctx.settings.changed.disconnect(__settings_changed)
198 ctx.settings.warn_about_empty_deletion = warn
199 ctx.settings.changed.connect(__settings_changed)
addons/kanban_tasks/view/settings/stages/stages.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://ccll34x5fagac
addons/kanban_tasks/view/settings/stages/stages.tscn +0 -144
……@@ -1,144 +0,0 @@
1[gd_scene load_steps=5 format=3 uid="uid://dapkpnkm8sow8"]
2
3[ext_resource type="Script" uid="uid://ccll34x5fagac" path="res://addons/kanban_tasks/view/settings/stages/stages.gd" id="1_1yycq"]
4
5[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_7cqkc"]
6content_margin_left = 4.0
7content_margin_top = 4.0
8content_margin_right = 4.0
9content_margin_bottom = 5.0
10bg_color = Color(0.1, 0.1, 0.1, 0.6)
11corner_radius_top_left = 3
12corner_radius_top_right = 3
13corner_radius_bottom_right = 3
14corner_radius_bottom_left = 3
15corner_detail = 5
16
17[sub_resource type="Image" id="Image_vr1jo"]
18data = {
19"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
20"format": "RGBA8",
21"height": 16,
22"mipmaps": false,
23"width": 16
24}
25
26[sub_resource type="ImageTexture" id="ImageTexture_frc8x"]
27image = SubResource("Image_vr1jo")
28
29[node name="Stages" type="VBoxContainer"]
30anchors_preset = 15
31anchor_right = 1.0
32anchor_bottom = 1.0
33grow_horizontal = 2
34grow_vertical = 2
35size_flags_horizontal = 3
36size_flags_vertical = 3
37script = ExtResource("1_1yycq")
38
39[node name="Header" type="HBoxContainer" parent="."]
40layout_mode = 2
41
42[node name="Label" type="Label" parent="Header"]
43layout_mode = 2
44size_flags_horizontal = 3
45text = "Edit Stage Layout:"
46
47[node name="WarnAboutEmptyDeletion" type="CheckBox" parent="Header"]
48unique_name_in_owner = true
49layout_mode = 2
50focus_mode = 0
51text = "Warn about empty deletion."
52
53[node name="PanelContainer" type="PanelContainer" parent="."]
54layout_mode = 2
55size_flags_vertical = 3
56theme_override_styles/panel = SubResource("StyleBoxFlat_7cqkc")
57
58[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer"]
59layout_mode = 2
60size_flags_horizontal = 3
61size_flags_vertical = 3
62
63[node name="CenterContainer" type="CenterContainer" parent="PanelContainer/ScrollContainer"]
64layout_mode = 2
65size_flags_horizontal = 3
66size_flags_vertical = 3
67
68[node name="Grid" type="HBoxContainer" parent="PanelContainer/ScrollContainer/CenterContainer"]
69layout_mode = 2
70theme_override_constants/separation = 5
71
72[node name="ColumnHolder" type="HBoxContainer" parent="PanelContainer/ScrollContainer/CenterContainer/Grid"]
73unique_name_in_owner = true
74layout_mode = 2
75theme_override_constants/separation = 5
76
77[node name="AddColumn" type="VBoxContainer" parent="PanelContainer/ScrollContainer/CenterContainer/Grid"]
78layout_mode = 2
79theme_override_constants/separation = 5
80
81[node name="AddColumn" type="Button" parent="PanelContainer/ScrollContainer/CenterContainer/Grid/AddColumn"]
82unique_name_in_owner = true
83custom_minimum_size = Vector2(40, 105)
84layout_mode = 2
85size_flags_vertical = 3
86focus_mode = 0
87
88[node name="Empty" type="Button" parent="PanelContainer/ScrollContainer/CenterContainer/Grid/AddColumn"]
89self_modulate = Color(1, 1, 1, 0)
90custom_minimum_size = Vector2(40, 40)
91layout_mode = 2
92text = "+"
93
94[node name="Warning" type="Control" parent="PanelContainer"]
95visible = false
96layout_mode = 2
97mouse_filter = 2
98
99[node name="WarningSign" type="Button" parent="PanelContainer/Warning"]
100unique_name_in_owner = true
101layout_mode = 0
102anchor_left = 1.0
103anchor_right = 1.0
104grow_horizontal = 0
105tooltip_text = "Adding to much stages can overflow the editor.
106
107Recommended maximum: 4*3"
108focus_mode = 0
109icon = SubResource("ImageTexture_frc8x")
110flat = true
111
112[node name="ConfirmNotEmpty" type="ConfirmationDialog" parent="."]
113unique_name_in_owner = true
114title = "Delete Stage"
115size = Vector2i(403, 159)
116
117[node name="VBoxContainer" type="VBoxContainer" parent="ConfirmNotEmpty"]
118anchors_preset = 15
119anchor_right = 1.0
120anchor_bottom = 1.0
121offset_left = 8.0
122offset_top = 8.0
123offset_right = -8.0
124offset_bottom = -49.0
125grow_horizontal = 2
126grow_vertical = 2
127
128[node name="Label" type="Label" parent="ConfirmNotEmpty/VBoxContainer"]
129layout_mode = 2
130text = "You are deleting a stage which has tasks assigned.
131
132The tasks will be assigned to the following stage:"
133
134[node name="TaskDestination" type="OptionButton" parent="ConfirmNotEmpty/VBoxContainer"]
135unique_name_in_owner = true
136layout_mode = 2
137size_flags_horizontal = 3
138
139[node name="ConfirmEmpty" type="ConfirmationDialog" parent="."]
140unique_name_in_owner = true
141title = "Delete Stage"
142size = Vector2i(316, 100)
143dialog_text = "Do you really want to delete this stage?
144You can not undo this."
addons/kanban_tasks/view/shortcuts.gd +0 -120
……@@ -1,120 +0,0 @@
1@tool
2extends Node
3
4
5var delete := Shortcut.new()
6var duplicate := Shortcut.new()
7var create := Shortcut.new()
8var rename := Shortcut.new()
9var search := Shortcut.new()
10var confirm := Shortcut.new()
11var undo := Shortcut.new()
12var redo := Shortcut.new()
13
14var save := Shortcut.new()
15var save_as := Shortcut.new()
16
17
18## Returns whether a specific node should handle the shortcut.
19static func should_handle_shortcut(node: Node) -> bool:
20 var focus_owner := node.get_viewport().gui_get_focus_owner()
21 return focus_owner and (node.is_ancestor_of(focus_owner) or focus_owner == node)
22
23
24func _ready() -> void:
25 __setup_shortcuts()
26
27
28func __setup_shortcuts() -> void:
29 # delete
30 var ev_delete := InputEventKey.new()
31 if OS.get_name() == "macOS":
32 ev_delete.keycode = KEY_BACKSPACE
33 ev_delete.meta_pressed = true
34 else:
35 ev_delete.keycode = KEY_DELETE
36 delete.events.append(ev_delete)
37
38 # duplicate
39 var ev_dupe := InputEventKey.new()
40 if OS.get_name() == "macOS":
41 ev_dupe.keycode = KEY_D
42 ev_dupe.meta_pressed = true
43 else:
44 ev_dupe.keycode = KEY_D
45 ev_dupe.ctrl_pressed = true
46 duplicate.events.append(ev_dupe)
47
48 # create
49 var ev_create := InputEventKey.new()
50 if OS.get_name() == "macOS":
51 ev_create.keycode = KEY_A
52 ev_create.meta_pressed = true
53 else:
54 ev_create.keycode = KEY_A
55 ev_create.ctrl_pressed = true
56 create.events.append(ev_create)
57
58 # rename
59 var ev_rename := InputEventKey.new()
60 ev_rename.keycode = KEY_F2
61 rename.events.append(ev_rename)
62
63 # search
64 var ev_search := InputEventKey.new()
65 if OS.get_name() == "macOS":
66 ev_search.keycode = KEY_F
67 ev_search.meta_pressed = true
68 else:
69 ev_search.keycode = KEY_F
70 ev_search.ctrl_pressed = true
71 search.events.append(ev_search)
72
73 # confirm
74 var ev_confirm := InputEventKey.new()
75 ev_confirm.keycode = KEY_ENTER
76 confirm.events.append(ev_confirm)
77
78 # undo
79 var ev_undo := InputEventKey.new()
80 if OS.get_name() == "macOS":
81 ev_undo.keycode = KEY_Z
82 ev_undo.meta_pressed = true
83 else:
84 ev_undo.keycode = KEY_Z
85 ev_undo.ctrl_pressed = true
86 undo.events.append(ev_undo)
87
88 # redo
89 var ev_redo := InputEventKey.new()
90 if OS.get_name() == "macOS":
91 ev_redo.keycode = KEY_Z
92 ev_redo.meta_pressed = true
93 ev_redo.shift_pressed = true
94 else:
95 ev_redo.keycode = KEY_Z
96 ev_redo.ctrl_pressed = true
97 ev_redo.shift_pressed = true
98 redo.events.append(ev_redo)
99
100 # save
101 var ev_save := InputEventKey.new()
102 if OS.get_name() == "macOS":
103 ev_save.keycode = KEY_S
104 ev_save.meta_pressed = true
105 else:
106 ev_save.keycode = KEY_S
107 ev_save.ctrl_pressed = true
108 save.events.append(ev_save)
109
110 # save as
111 var ev_save_as := InputEventKey.new()
112 if OS.get_name() == "macOS":
113 ev_save_as.keycode = KEY_S
114 ev_save_as.meta_pressed = true
115 ev_save_as.shift_pressed = true
116 else:
117 ev_save_as.keycode = KEY_S
118 ev_save_as.ctrl_pressed = true
119 ev_save_as.shift_pressed = true
120 save_as.events.append(ev_save_as)
addons/kanban_tasks/view/shortcuts.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://ci13ff1lsstcu
addons/kanban_tasks/view/stage/stage.gd +0 -266
……@@ -1,266 +0,0 @@
1@tool
2extends MarginContainer
3
4## The visual representation of a stage.
5
6
7const __Singletons := preload("../../plugin_singleton/singletons.gd")
8const __Shortcuts := preload("../shortcuts.gd")
9const __EditContext := preload("../edit_context.gd")
10const __TaskData := preload("../../data/task.gd")
11const __TaskScene := preload("../task/task.tscn")
12const __TaskScript := preload("../task/task.gd")
13const __EditLabel := preload("../../edit_label/edit_label.gd")
14const __BoardData := preload("../../data/board.gd")
15const __CategoryPopupMenu := preload("../category/category_popup_menu.gd")
16
17var board_data: __BoardData
18var data_uuid: String
19
20var __category_menu := __CategoryPopupMenu.new()
21
22@onready var panel_container: PanelContainer = %Panel
23@onready var title_label: __EditLabel = %Title
24@onready var create_button: Button = %Create
25@onready var task_holder: VBoxContainer = %TaskHolder
26@onready var scroll_container: ScrollContainer = %ScrollContainer
27@onready var preview: Control = %Preview
28@onready var preview_color: ColorRect = %Preview/Color
29
30
31func _ready() -> void:
32 update()
33 board_data.get_stage(data_uuid).changed.connect(update.bind(true))
34
35 scroll_container.set_drag_forwarding(
36 _get_drag_data_fw.bind(scroll_container),
37 _can_drop_data_fw.bind(scroll_container),
38 _drop_data_fw.bind(scroll_container),
39 )
40
41 create_button.pressed.connect(__on_create_button_pressed)
42 add_child(__category_menu)
43 __category_menu.uuid_selected.connect(__on_category_create_popup_uuid_selected)
44 __category_menu.popup_hide.connect(create_button.set_pressed_no_signal.bind(false))
45
46 notification(NOTIFICATION_THEME_CHANGED)
47
48 await get_tree().create_timer(0.0).timeout
49 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
50 if ctx.focus == data_uuid:
51 ctx.focus = ""
52 grab_focus()
53
54
55func _input(event: InputEvent) -> void:
56 if event is InputEventMouseMotion:
57 if not Rect2(Vector2(), size).has_point(get_local_mouse_position()):
58 preview.visible = false
59
60
61func _shortcut_input(event: InputEvent) -> void:
62 if not __Shortcuts.should_handle_shortcut(self):
63 return
64 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
65 if not event.is_echo() and event.is_pressed():
66 if shortcuts.create.matches_event(event):
67 get_viewport().set_input_as_handled()
68
69 __category_menu.popup_at_mouse_position(self)
70
71 elif shortcuts.rename.matches_event(event):
72 get_viewport().set_input_as_handled()
73 title_label.show_edit()
74
75
76func _can_drop_data(at_position: Vector2, data: Variant) -> bool:
77 preview.visible = true
78 preview.position.y = __target_height_from_position(at_position)
79
80 return data is Dictionary and data.has("task") and data.has("stage")
81
82
83func _can_drop_data_fw(at_position: Vector2, data: Variant, from: Control) -> bool:
84 var local_pos = (at_position + from.get_global_rect().position) - get_global_rect().position
85 return _can_drop_data(local_pos, data)
86
87
88func _get_drag_data_fw(at_position: Vector2, from: Control) -> Variant:
89 if from is __TaskScript:
90 var control := Control.new()
91 var rect := ColorRect.new()
92 control.add_child(rect)
93 rect.size = from.get_rect().size
94 rect.position = -at_position
95 rect.color = board_data.get_category(board_data.get_task(from.data_uuid).category).color
96 from.set_drag_preview(control)
97
98 return {
99 "task": from.data_uuid,
100 "stage": data_uuid,
101 }
102 return null
103
104
105func _drop_data(at_position: Vector2, data: Variant) -> void:
106 var index := __target_index_from_position(at_position)
107 preview.hide()
108
109 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
110 ctx.undo_redo.create_action("Move task")
111
112 var tasks := board_data.get_stage(data["stage"]).tasks
113
114 if data["stage"] == data_uuid:
115 var old_index := tasks.find(data["task"])
116 if index < old_index:
117 tasks.erase(data["task"])
118 tasks.insert(index, data["task"])
119 elif index > old_index + 1:
120 tasks.erase(data["task"])
121 tasks.insert(index - 1, data["task"])
122 else:
123 tasks.erase(data["task"])
124
125 ctx.undo_redo.add_do_property(board_data.get_stage(data["stage"]), &"tasks", tasks.duplicate())
126 ctx.undo_redo.add_undo_property(board_data.get_stage(data["stage"]), &"tasks", board_data.get_stage(data["stage"]).tasks)
127
128 tasks = board_data.get_stage(data_uuid).tasks
129 tasks.insert(index, data["task"])
130
131 ctx.focus = data["task"]
132
133 ctx.undo_redo.add_do_property(board_data.get_stage(data_uuid), &"tasks", tasks)
134 ctx.undo_redo.add_undo_property(board_data.get_stage(data_uuid), &"tasks", board_data.get_stage(data_uuid).tasks)
135 ctx.undo_redo.commit_action()
136
137
138func _drop_data_fw(at_position: Vector2, data: Variant, from: Control) -> void:
139 var local_pos = (at_position + from.get_global_rect().position) - get_global_rect().position
140 _drop_data(local_pos, data)
141
142
143func _notification(what: int) -> void:
144 match(what):
145 NOTIFICATION_THEME_CHANGED:
146 if is_instance_valid(panel_container):
147 panel_container.add_theme_stylebox_override(&"panel", get_theme_stylebox(&"panel", &"Tree"))
148 if is_instance_valid(create_button):
149 create_button.icon = get_theme_icon(&"Add", &"EditorIcons")
150 if is_instance_valid(preview_color):
151 preview_color.color = get_theme_color(&"font_selected_color", &"TabBar")
152
153
154func update(single: bool = false) -> void:
155 var focus_owner := get_viewport().gui_get_focus_owner()
156 if single:
157 grab_focus()
158
159 if title_label.text_changed.is_connected(__set_title):
160 title_label.text_changed.disconnect(__set_title)
161 title_label.text = board_data.get_stage(data_uuid).title
162 title_label.text_changed.connect(__set_title)
163
164 var old_scroll := scroll_container.scroll_vertical
165
166 if is_instance_valid(focus_owner) and (is_ancestor_of(focus_owner) or focus_owner == self):
167 if focus_owner is __TaskScript:
168 __Singletons.instance_of(__EditContext, self).focus = focus_owner.data_uuid
169
170 for task in task_holder.get_children():
171 task.queue_free()
172
173 for uuid in board_data.get_stage(data_uuid).tasks:
174 var task: __TaskScript = __TaskScene.instantiate()
175 task.board_data = board_data
176 task.data_uuid = uuid
177 task.set_drag_forwarding(
178 _get_drag_data_fw.bind(task),
179 _can_drop_data_fw.bind(task),
180 _drop_data_fw.bind(task),
181 )
182 task_holder.add_child(task)
183
184 scroll_container.scroll_vertical = old_scroll
185 __update_category_menus()
186
187
188func __update_category_menus() -> void:
189 __category_menu.board_data = board_data
190
191
192func __target_index_from_position(pos: Vector2) -> int:
193 var global_pos := pos + get_global_position()
194
195 if not scroll_container.get_global_rect().has_point(global_pos):
196 return 0
197
198 var scroll_pos := global_pos - task_holder.get_global_position()
199 var c := 0
200 for task in task_holder.get_children():
201 var y = task.position.y + task.size.y/2
202 if scroll_pos.y < y:
203 return c
204 c += 1
205
206 return task_holder.get_child_count()
207
208
209func __set_title(value: String) -> void:
210 board_data.get_stage(data_uuid).title = value
211
212
213func __on_create_button_pressed() -> void:
214 if board_data.get_category_count() > 1:
215 __category_menu.popup_at_local_position(create_button, Vector2(0, create_button.get_global_rect().size.y))
216 else:
217 __create_task(board_data.get_categories()[0])
218 create_button.set_pressed_no_signal(false)
219
220
221func __create_task(category: String) -> void:
222 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
223 var stage_data := board_data.get_stage(data_uuid)
224
225 var task_data := __TaskData.new("New task", "", category)
226 var uuid = board_data.add_task(task_data)
227 var tasks = stage_data.tasks
228 tasks.append(uuid)
229
230 ctx.undo_redo.create_action("Add task")
231 ctx.undo_redo.add_do_method(board_data.__add_task.bind(task_data, uuid))
232 ctx.undo_redo.add_do_property(stage_data, &"tasks", tasks)
233 ctx.undo_redo.add_undo_property(stage_data, &"tasks", stage_data.tasks)
234 ctx.undo_redo.add_undo_method(board_data.remove_task.bind(uuid))
235 ctx.undo_redo.commit_action(false)
236
237 stage_data.tasks = tasks
238
239 for task in task_holder.get_children():
240 if task.data_uuid == uuid:
241 await get_tree().create_timer(0.0).timeout
242 task.grab_focus()
243 task.show_edit(__EditLabel.INTENTION.REPLACE)
244
245 ctx.filter = null
246
247
248func __target_height_from_position(pos: Vector2) -> float:
249 var global_pos = pos + get_global_position()
250
251 if not scroll_container.get_global_rect().has_point(global_pos):
252 return - float(task_holder.get_theme_constant(&"separation")) / 2.0
253
254 var scroll_pos: Vector2 = global_pos - task_holder.get_global_position()
255 var c := 0.0
256 for task in task_holder.get_children():
257 var y = task.position.y + task.size.y/2.0
258 if scroll_pos.y < y:
259 return c - float(task_holder.get_theme_constant(&"separation")) / 2.0
260 c += task.size.y + task_holder.get_theme_constant(&"separation")
261
262 return c
263
264
265func __on_category_create_popup_uuid_selected(uuid) -> void:
266 __create_task(uuid)
addons/kanban_tasks/view/stage/stage.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://b2ngyvkovxfnp
addons/kanban_tasks/view/stage/stage.tscn +0 -138
……@@ -1,138 +0,0 @@
1[gd_scene load_steps=6 format=3 uid="uid://bjmtdjfx7iqgp"]
2
3[ext_resource type="Script" uid="uid://b2ngyvkovxfnp" path="res://addons/kanban_tasks/view/stage/stage.gd" id="1_i5556"]
4[ext_resource type="Script" uid="uid://bj3jwa1phfj3t" path="res://addons/kanban_tasks/edit_label/edit_label.gd" id="2"]
5
6[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h7hiu"]
7content_margin_left = 4.0
8content_margin_top = 4.0
9content_margin_right = 4.0
10content_margin_bottom = 5.0
11bg_color = Color(0.1, 0.1, 0.1, 0.6)
12corner_radius_top_left = 3
13corner_radius_top_right = 3
14corner_radius_bottom_right = 3
15corner_radius_bottom_left = 3
16corner_detail = 5
17
18[sub_resource type="Image" id="Image_y70vq"]
19data = {
20"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 131, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 128, 128, 4, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 93, 93, 55, 255, 97, 97, 58, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 98, 98, 47, 255, 97, 97, 42, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 94, 94, 46, 255, 93, 93, 236, 255, 93, 93, 233, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 93, 93, 252, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
21"format": "RGBA8",
22"height": 16,
23"mipmaps": false,
24"width": 16
25}
26
27[sub_resource type="ImageTexture" id="ImageTexture_yu4gn"]
28image = SubResource("Image_y70vq")
29
30[node name="Stage" type="MarginContainer"]
31editor_description = "This container is needed because the panel style cannot be updated from a script on the panel container."
32custom_minimum_size = Vector2(200, 200)
33size_flags_horizontal = 3
34size_flags_vertical = 3
35focus_mode = 1
36theme_override_constants/margin_left = 0
37theme_override_constants/margin_top = 0
38theme_override_constants/margin_right = 0
39theme_override_constants/margin_bottom = 0
40script = ExtResource("1_i5556")
41
42[node name="Panel" type="PanelContainer" parent="."]
43unique_name_in_owner = true
44layout_mode = 2
45size_flags_vertical = 3
46mouse_filter = 2
47theme_override_styles/panel = SubResource("StyleBoxFlat_h7hiu")
48
49[node name="VBoxContainer" type="VBoxContainer" parent="Panel"]
50layout_mode = 2
51mouse_filter = 2
52theme_override_constants/separation = 5
53
54[node name="HBoxContainer" type="HBoxContainer" parent="Panel/VBoxContainer"]
55layout_mode = 2
56mouse_filter = 2
57theme_override_constants/separation = 0
58
59[node name="Title" type="VBoxContainer" parent="Panel/VBoxContainer/HBoxContainer"]
60unique_name_in_owner = true
61custom_minimum_size = Vector2(0, 31)
62layout_mode = 2
63size_flags_horizontal = 3
64size_flags_vertical = 4
65alignment = 1
66script = ExtResource("2")
67default_intention = 0
68
69[node name="Create" type="Button" parent="Panel/VBoxContainer/HBoxContainer"]
70unique_name_in_owner = true
71layout_mode = 2
72tooltip_text = "Add task."
73focus_mode = 0
74toggle_mode = true
75action_mode = 0
76icon = SubResource("ImageTexture_yu4gn")
77
78[node name="HSeparator" type="HSeparator" parent="Panel/VBoxContainer"]
79layout_mode = 2
80mouse_filter = 2
81theme_override_constants/separation = 0
82
83[node name="ScrollContainer" type="ScrollContainer" parent="Panel/VBoxContainer"]
84unique_name_in_owner = true
85layout_mode = 2
86size_flags_horizontal = 3
87size_flags_vertical = 3
88follow_focus = true
89horizontal_scroll_mode = 0
90
91[node name="MarginContainer" type="MarginContainer" parent="Panel/VBoxContainer/ScrollContainer"]
92layout_mode = 2
93size_flags_horizontal = 3
94mouse_filter = 2
95theme_override_constants/margin_left = 5
96theme_override_constants/margin_top = 5
97theme_override_constants/margin_right = 5
98theme_override_constants/margin_bottom = 5
99
100[node name="TaskHolder" type="VBoxContainer" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer"]
101unique_name_in_owner = true
102layout_mode = 2
103size_flags_horizontal = 3
104mouse_filter = 2
105theme_override_constants/separation = 5
106
107[node name="PreviewHolder" type="Control" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer"]
108layout_mode = 2
109size_flags_horizontal = 3
110size_flags_vertical = 0
111mouse_filter = 2
112
113[node name="Preview" type="Control" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer/PreviewHolder"]
114unique_name_in_owner = true
115visible = false
116layout_mode = 1
117anchors_preset = 14
118anchor_top = 0.5
119anchor_right = 1.0
120anchor_bottom = 0.5
121grow_horizontal = 2
122grow_vertical = 2
123size_flags_horizontal = 0
124size_flags_vertical = 0
125
126[node name="Color" type="ColorRect" parent="Panel/VBoxContainer/ScrollContainer/MarginContainer/PreviewHolder/Preview"]
127custom_minimum_size = Vector2(0, 1)
128layout_mode = 1
129anchors_preset = 14
130anchor_top = 0.5
131anchor_right = 1.0
132anchor_bottom = 0.5
133offset_top = -0.5
134offset_bottom = 0.5
135grow_horizontal = 2
136grow_vertical = 2
137mouse_filter = 2
138color = Color(0.95, 0.95, 0.95, 1)
addons/kanban_tasks/view/start/start.gd +0 -59
……@@ -1,59 +0,0 @@
1@tool
2extends Control
3
4
5const __Singletons := preload("../../plugin_singleton/singletons.gd")
6const __EditContext := preload("../edit_context.gd")
7
8signal create_board()
9signal open_board(path: String)
10
11@onready var create_board_button: LinkButton = %CreateBoard
12@onready var open_board_button: LinkButton = %OpenBoard
13@onready var recent_board_holder: VBoxContainer = %RecentBoardHolder
14@onready var delete_from_recent_dialog: ConfirmationDialog = %DeleteFromRecent
15
16
17func _ready() -> void:
18 create_board_button.pressed.connect(func(): create_board.emit())
19 open_board_button.pressed.connect(func(): open_board.emit(""))
20 await get_tree().create_timer(0.0).timeout
21 await get_tree().create_timer(0.0).timeout
22 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
23 ctx.settings.changed.connect(update)
24 update()
25
26
27func update() -> void:
28 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
29
30 for child in recent_board_holder.get_children():
31 child.queue_free()
32
33 for board in ctx.settings.recent_files:
34 var button := LinkButton.new()
35 button.underline = LinkButton.UNDERLINE_MODE_NEVER
36 button.text = board
37 button.add_theme_color_override(&"font_color", Color(1, 1, 1, 0.2))
38 button.pressed.connect(__on_open_recent.bind(board))
39 recent_board_holder.add_child(button)
40
41
42func __delete_from_recent(path: String) -> void:
43 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self) as __EditContext
44
45 var recent = ctx.settings.recent_files
46 var i = recent.find(path)
47 if i >= 0:
48 recent.remove_at(i)
49 ctx.settings.recent_files = recent
50
51
52func __on_open_recent(path: String) -> void:
53 if not FileAccess.file_exists(path):
54 if delete_from_recent_dialog.confirmed.is_connected(__delete_from_recent):
55 delete_from_recent_dialog.confirmed.disconnect(__delete_from_recent)
56 delete_from_recent_dialog.confirmed.connect(__delete_from_recent.bind(path))
57 delete_from_recent_dialog.popup_centered()
58 return
59 open_board.emit(path)
addons/kanban_tasks/view/start/start.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://dkim1ufdoyfwj
addons/kanban_tasks/view/start/start.tscn +0 -103
……@@ -1,103 +0,0 @@
1[gd_scene load_steps=4 format=3 uid="uid://bemcl1rqpeqty"]
2
3[ext_resource type="Script" uid="uid://dkim1ufdoyfwj" path="res://addons/kanban_tasks/view/start/start.gd" id="1_fkeby"]
4
5[sub_resource type="LabelSettings" id="LabelSettings_0i4nn"]
6font_size = 22
7
8[sub_resource type="LabelSettings" id="LabelSettings_5febo"]
9font_size = 20
10font_color = Color(1, 1, 1, 0.384314)
11
12[node name="Start" type="Control"]
13layout_mode = 3
14anchors_preset = 15
15anchor_right = 1.0
16anchor_bottom = 1.0
17grow_horizontal = 2
18grow_vertical = 2
19script = ExtResource("1_fkeby")
20
21[node name="CenterContainer" type="CenterContainer" parent="."]
22layout_mode = 1
23anchors_preset = 15
24anchor_right = 1.0
25anchor_bottom = 1.0
26grow_horizontal = 2
27grow_vertical = 2
28
29[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer"]
30layout_mode = 2
31theme_override_constants/separation = 40
32
33[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HBoxContainer"]
34layout_mode = 2
35theme_override_constants/separation = 11
36
37[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HBoxContainer/VBoxContainer"]
38layout_mode = 2
39theme_override_constants/separation = -10
40
41[node name="Label" type="Label" parent="CenterContainer/HBoxContainer/VBoxContainer/VBoxContainer"]
42layout_mode = 2
43text = "Kanban Tasks"
44label_settings = SubResource("LabelSettings_0i4nn")
45
46[node name="Label2" type="Label" parent="CenterContainer/HBoxContainer/VBoxContainer/VBoxContainer"]
47layout_mode = 2
48text = "Todo Manager"
49label_settings = SubResource("LabelSettings_5febo")
50
51[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/HBoxContainer/VBoxContainer"]
52layout_mode = 2
53theme_override_constants/margin_left = 5
54theme_override_constants/margin_top = 0
55theme_override_constants/margin_right = 0
56theme_override_constants/margin_bottom = 0
57
58[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/HBoxContainer/VBoxContainer/MarginContainer"]
59layout_mode = 2
60theme_override_constants/separation = 0
61
62[node name="CreateBoard" type="LinkButton" parent="CenterContainer/HBoxContainer/VBoxContainer/MarginContainer/VBoxContainer"]
63unique_name_in_owner = true
64layout_mode = 2
65text = "Create Board"
66underline = 2
67
68[node name="OpenBoard" type="LinkButton" parent="CenterContainer/HBoxContainer/VBoxContainer/MarginContainer/VBoxContainer"]
69unique_name_in_owner = true
70layout_mode = 2
71text = "Open Board"
72underline = 2
73
74[node name="VSeparator" type="VSeparator" parent="CenterContainer/HBoxContainer"]
75layout_mode = 2
76
77[node name="VBoxContainer2" type="VBoxContainer" parent="CenterContainer/HBoxContainer"]
78layout_mode = 2
79
80[node name="Label" type="Label" parent="CenterContainer/HBoxContainer/VBoxContainer2"]
81layout_mode = 2
82text = "Recent Boards"
83
84[node name="MarginContainer2" type="MarginContainer" parent="CenterContainer/HBoxContainer/VBoxContainer2"]
85layout_mode = 2
86theme_override_constants/margin_left = 5
87theme_override_constants/margin_top = 0
88theme_override_constants/margin_right = 0
89theme_override_constants/margin_bottom = 0
90
91[node name="RecentBoardHolder" type="VBoxContainer" parent="CenterContainer/HBoxContainer/VBoxContainer2/MarginContainer2"]
92unique_name_in_owner = true
93layout_mode = 2
94theme_override_constants/separation = 0
95
96[node name="DeleteFromRecent" type="ConfirmationDialog" parent="."]
97unique_name_in_owner = true
98title = "Board not found"
99size = Vector2i(388, 106)
100ok_button_text = "Delete"
101dialog_text = "The board does not seem to exist anymore.
102You may choos to remove it from the recent list."
103cancel_button_text = "Keep"
addons/kanban_tasks/view/task/autosize_label.gd +0 -32
……@@ -1,32 +0,0 @@
1@tool
2extends Label
3
4
5@export var auto_size_height: bool = true:
6 set(value):
7 auto_size_height = value
8 queue_redraw()
9
10
11func _init() -> void:
12 draw.connect(__before_draw)
13
14
15func __before_draw() -> void:
16 # This is needed if wrapping is turned on in an autosized label,
17 # otherwise the conatiner will give 0 height
18 # (As the label itself cannot decide what size to ask from the container
19 # as due to wrapping, no size is fixed. But fortunatelly the label
20 # makes internal calculation according to intended width before the draw)
21 if auto_size_height:
22 var stylebox := get_theme_stylebox(&"normal")
23 var line_spacing = get_theme_constant(&"line_spacing")
24 var height := max(0, stylebox.content_margin_top)
25 for i in get_line_count():
26 if max_lines_visible >= 0 and i >= max_lines_visible:
27 break
28 if i > 0:
29 height += line_spacing
30 height += get_line_height(i)
31 height += max(0, stylebox.content_margin_bottom)
32 custom_minimum_size.y = height
addons/kanban_tasks/view/task/autosize_label.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://bau1py83aff06
addons/kanban_tasks/view/task/task.gd +0 -439
……@@ -1,439 +0,0 @@
1@tool
2extends MarginContainer
3
4## The visual representation of a task.
5
6
7const __Singletons := preload("../../plugin_singleton/singletons.gd")
8const __Shortcuts := preload("../shortcuts.gd")
9const __EditContext := preload("../edit_context.gd")
10const __Filter := preload("../filter.gd")
11const __BoardData := preload("../../data/board.gd")
12const __EditLabel := preload("../../edit_label/edit_label.gd")
13const __ExpandButton := preload("../../expand_button/expand_button.gd")
14const __TaskData := preload("../../data/task.gd")
15const __DetailsScript := preload("../details/details.gd")
16const __StepHolder := preload("../details/step_holder.gd")
17const __TooltipScript := preload("../tooltip.gd")
18const __CategoryPopupMenu := preload("../category/category_popup_menu.gd")
19
20enum ACTIONS {
21 DETAILS,
22 RENAME,
23 DELETE,
24 DUPLICATE,
25}
26
27const COLOR_WIDTH: int = 8
28
29var board_data: __BoardData:
30 set(value):
31 board_data = value
32 __update_category_menu()
33var data_uuid: String
34
35var __style_focus: StyleBoxFlat
36var __style_panel: StyleBoxFlat
37
38var __category_menu := __CategoryPopupMenu.new()
39
40@onready var panel_container: PanelContainer = %Panel
41@onready var category_button: Button = %CategoryButton
42@onready var title_label: __EditLabel = %Title
43@onready var description_label: Label = %Description
44@onready var step_holder: __StepHolder = %StepHolder
45@onready var expand_button: __ExpandButton = %ExpandButton
46@onready var edit_button: Button = %Edit
47@onready var context_menu: PopupMenu = %ContextMenu
48@onready var details: __DetailsScript = %Details
49
50
51func _ready() -> void:
52 __style_focus = StyleBoxFlat.new()
53 __style_focus.set_border_width_all(1)
54 __style_focus.draw_center = false
55
56 __style_panel = StyleBoxFlat.new()
57 __style_panel.set_border_width_all(0)
58 __style_panel.border_width_left = COLOR_WIDTH
59 __style_panel.draw_center = false
60 panel_container.add_theme_stylebox_override(&"panel", __style_panel)
61
62 context_menu.id_pressed.connect(__action)
63 edit_button.pressed.connect(__action.bind(ACTIONS.DETAILS))
64 expand_button.state_changed.connect(func (expanded): __update_step_holder())
65
66 category_button.pressed.connect(__on_category_button_pressed)
67 add_child(__category_menu)
68 __category_menu.uuid_selected.connect(__on_category_menu_uuid_selected)
69
70 notification(NOTIFICATION_THEME_CHANGED)
71
72 await get_tree().create_timer(0.0).timeout
73 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
74
75 update()
76 board_data.get_task(data_uuid).changed.connect(update)
77 board_data.changed.connect(__update_category_button)
78
79 if data_uuid == ctx.focus:
80 ctx.focus = ""
81 grab_focus()
82
83 if not ctx.filter_changed.is_connected(__apply_filter):
84 ctx.filter_changed.connect(__apply_filter)
85 ctx.settings.changed.connect(update)
86 __apply_filter()
87
88
89func _gui_input(event: InputEvent) -> void:
90 if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT:
91 accept_event()
92 __update_context_menu()
93 context_menu.position = get_global_mouse_position()
94 if not get_window().gui_embed_subwindows:
95 context_menu.position += get_window().position
96 context_menu.popup()
97
98 if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.is_pressed() and event.is_double_click():
99 __action(ACTIONS.DETAILS)
100
101
102func _shortcut_input(event: InputEvent) -> void:
103 if not __Shortcuts.should_handle_shortcut(self):
104 return
105 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
106 if not event.is_echo() and event.is_pressed():
107 if shortcuts.delete.matches_event(event):
108 get_viewport().set_input_as_handled()
109 __action(ACTIONS.DELETE)
110 elif shortcuts.confirm.matches_event(event):
111 get_viewport().set_input_as_handled()
112 __action(ACTIONS.DETAILS)
113 elif shortcuts.rename.matches_event(event):
114 get_viewport().set_input_as_handled()
115 __action(ACTIONS.RENAME)
116 elif shortcuts.duplicate.matches_event(event):
117 get_viewport().set_input_as_handled()
118 __action(ACTIONS.DUPLICATE)
119
120
121func _make_custom_tooltip(for_text) -> Object:
122 var tooltip := __TooltipScript.new()
123 tooltip.text = for_text
124 tooltip.mimic_paragraphs()
125 return tooltip
126
127
128func _notification(what: int) -> void:
129 match(what):
130 NOTIFICATION_THEME_CHANGED:
131 if panel_container:
132 var tab_panel = get_theme_stylebox(&"panel", &"TabContainer")
133 if tab_panel is StyleBoxFlat:
134 __style_panel.bg_color = tab_panel.bg_color
135 __style_panel.draw_center = true
136 else:
137 __style_panel.draw_center = false
138 if edit_button:
139 edit_button.icon = get_theme_icon(&"Edit", &"EditorIcons")
140 NOTIFICATION_DRAW:
141 if has_focus():
142 __style_focus.draw(
143 get_canvas_item(),
144 Rect2(
145 panel_container.get_global_rect().position - get_global_rect().position,
146 panel_container.size
147 ),
148 )
149
150
151func update() -> void:
152 if not is_inside_tree():
153 # The node might linger in the undoredo manager.
154 return
155
156 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
157 var task := board_data.get_task(data_uuid)
158 var task_category := board_data.get_category(task.category)
159
160 __style_focus.border_color = task_category.color
161 __style_panel.border_color = task_category.color
162
163 category_button.text = task_category.title
164 category_button.visible = ctx.settings.show_category_on_board
165
166 if ctx.settings.show_description_preview:
167 var description: String
168 match ctx.settings.description_on_board:
169 ctx.settings.DescriptionOnBoard.FIRST_LINE:
170 description = task.description
171 var idx := description.find("\n")
172 description = description.substr(0, idx)
173 ctx.settings.DescriptionOnBoard.UNTIL_FIRST_BLANK_LINE:
174 description = task.description
175 var idx := description.find("\n\n")
176 description = description.substr(0, idx)
177 _:
178 description = task.description
179 description_label.text = description
180 if ctx.settings.max_displayed_lines_in_description > 0:
181 description_label.max_lines_visible = ctx.settings.max_displayed_lines_in_description
182 else:
183 description_label.max_lines_visible = -1
184 description_label.visible = ctx.settings.show_description_preview and description_label.text.strip_edges().length() != 0
185 else:
186 description_label.text = ""
187 description_label.visible = (description_label.text.length() > 0)
188
189 __update_step_holder()
190
191 var steps := board_data.get_task(data_uuid).steps
192 for step in steps:
193 if not step.changed.is_connected(__update_step_holder):
194 step.changed.connect(__update_step_holder)
195
196 if title_label.text_changed.is_connected(__set_title):
197 title_label.text_changed.disconnect(__set_title)
198 title_label.text = board_data.get_task(data_uuid).title
199 title_label.text_changed.connect(__set_title)
200
201 __update_category_menu()
202 __update_category_button()
203 __update_tooltip()
204
205 queue_redraw()
206
207
208func show_edit(intention: __EditLabel.INTENTION) -> void:
209 title_label.show_edit(intention)
210
211
212func __update_step_holder() -> void:
213 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
214 var task := board_data.get_task(data_uuid)
215 var expanded := expand_button.expanded
216
217 step_holder.clear_steps()
218 var step_count := 0
219 var expandable := false
220
221 if ctx.settings.show_steps_preview:
222 var steps := board_data.get_task(data_uuid).steps
223 var max_step_count := ctx.settings.max_steps_on_board
224 match ctx.settings.steps_on_board:
225 ctx.settings.StepsOnBoard.ONLY_OPEN:
226 for i in steps.size():
227 if steps[i].done:
228 continue
229 if max_step_count > 0 and step_count >= max_step_count:
230 expandable = true
231 if not expanded:
232 break
233 step_holder.add_step(steps[i])
234 step_count += 1
235 ctx.settings.StepsOnBoard.ALL_OPEN_FIRST:
236 for i in steps.size():
237 if steps[i].done:
238 continue
239 if max_step_count > 0 and step_count >= max_step_count:
240 expandable = true
241 if not expanded:
242 break
243 step_holder.add_step(steps[i])
244 step_count += 1
245 for i in steps.size():
246 if not steps[i].done:
247 continue
248 if max_step_count > 0 and step_count >= max_step_count:
249 expandable = true
250 if not expanded:
251 break
252 step_holder.add_step(steps[i])
253 step_count += 1
254 ctx.settings.StepsOnBoard.ALL_IN_ORDER:
255 for i in steps.size():
256 if max_step_count > 0 and step_count >= max_step_count:
257 expandable = true
258 if not expanded:
259 break
260 step_holder.add_step(steps[i])
261 step_count += 1
262 _:
263 pass
264 step_holder.visible = (step_count > 0)
265 expand_button.visible = expandable
266
267
268func __update_category_menu() -> void:
269 __category_menu.board_data = board_data
270
271
272func __update_category_button() -> void:
273 if board_data.get_category_count() > 1:
274 category_button.mouse_filter = Control.MOUSE_FILTER_STOP
275 else:
276 category_button.mouse_filter = Control.MOUSE_FILTER_IGNORE
277
278
279func __update_tooltip() -> void:
280 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
281 var task := board_data.get_task(data_uuid)
282 var task_category := board_data.get_category(task.category)
283 var steps := board_data.get_task(data_uuid).steps
284
285 var category_bullet = "[bgcolor=#" + task_category.color.to_html(false) + "] [/bgcolor] "
286 #var category_bullet = "[color=#" + task_category.color.to_html(false) + "]\u2588\u2588[/color]"
287 #var category_bullet = "[color=#" + task_category.color.to_html(false) + "]\u220E[/color]"
288 #var category_bullet = "[color=#" + task_category.color.to_html(false) + "]\u25A0[/color]"
289 tooltip_text = category_bullet + " " + board_data.get_category(task.category).title + ": " + task.title
290 if task.description !=null and task.description.length() > 0:
291 tooltip_text += "[p]" + task.description + "[/p]"
292
293 var open_steps = []
294 var done_steps = []
295 for step in steps:
296 (done_steps if step.done else open_steps).append(step)
297 #var open_step_bullet = "\u25A1" # Unfilled square
298 #var open_step_bullet = "[color=#808080]\u25A0[/color]" # Filled gray square
299 #var done_step_bullet = "\u25A0" # Filled square
300 #var open_step_bullet = "\u2718" # Heavy ballot X
301 #var done_step_bullet = "\u2714" # Heavy check mark
302 #var open_step_bullet = "\u2717" # Ballot X
303 #var done_step_bullet = "\u2713" # Check mark
304 var open_step_bullet = "[color=#F08080]\u25A0[/color]" # Filled red square
305 var done_step_bullet = "[color=#98FB98]\u25A0[/color]" # Filled green square
306 if open_steps.size() > 0 or done_steps.size() > 0:
307 tooltip_text += "[p]"
308 if open_steps.size() > 0:
309 tooltip_text += "Open steps:\n[table=2]"
310 for step in open_steps:
311 tooltip_text += "[cell]" + open_step_bullet + "[/cell][cell]" + step.details + "[/cell]\n"
312 tooltip_text += "[/table]\n"
313 if done_steps.size() > 0:
314 tooltip_text += "Done steps:\n[table=2]"
315 for step in done_steps:
316 tooltip_text += "[cell]" + done_step_bullet + "[/cell][cell]" + step.details + "[/cell]\n"
317 tooltip_text += "[/table]\n"
318 tooltip_text += "[/p]"
319
320
321func __apply_filter() -> void:
322 var ctx: __EditContext = __Singletons.instance_of(__EditContext, self)
323
324 if not ctx.filter or ctx.filter.text.length() == 0:
325 show()
326 return
327
328 var task = board_data.get_task(data_uuid)
329 var filter_simple := __simplify_string(ctx.filter.text)
330 var filter_matches := false
331 if not filter_matches:
332 var text_simple := __simplify_string(task.title)
333 if text_simple.matchn("*" + filter_simple + "*"):
334 filter_matches = true
335 if not filter_matches:
336 var category = board_data.get_category(task.category)
337 var text_simple := __simplify_string(category.title)
338 if text_simple.matchn("*" + filter_simple + "*"):
339 filter_matches = true
340 if not filter_matches and ctx.filter.advanced:
341 var text_simple := __simplify_string(task.description)
342 if text_simple.matchn("*" + filter_simple + "*"):
343 filter_matches = true
344 if not filter_matches and ctx.filter.advanced:
345 for step in task.steps:
346 if not filter_matches:
347 var text_simple := __simplify_string(step.details)
348 if text_simple.matchn("*" + filter_simple + "*"):
349 filter_matches = true
350 else:
351 break
352
353 if filter_matches:
354 show()
355 else:
356 hide()
357
358
359func __simplify_string(string: String) -> String:
360 return string.replace(" ", "").replace("\t", "")
361
362
363func __update_context_menu() -> void:
364 var shortcuts: __Shortcuts = __Singletons.instance_of(__Shortcuts, self)
365
366 context_menu.clear()
367 context_menu.add_item("Details", ACTIONS.DETAILS)
368
369 context_menu.add_separator()
370
371 context_menu.add_icon_item(get_theme_icon(&"Rename", &"EditorIcons"), "Rename", ACTIONS.RENAME)
372 context_menu.set_item_shortcut(context_menu.get_item_index(ACTIONS.RENAME), shortcuts.rename)
373
374 context_menu.add_icon_item(get_theme_icon(&"Duplicate", &"EditorIcons"), "Duplicate", ACTIONS.DUPLICATE)
375 context_menu.set_item_shortcut(context_menu.get_item_index(ACTIONS.DUPLICATE), shortcuts.duplicate)
376
377 context_menu.add_icon_item(get_theme_icon(&"Remove", &"EditorIcons"), "Delete", ACTIONS.DELETE)
378 context_menu.set_item_shortcut(context_menu.get_item_index(ACTIONS.DELETE), shortcuts.delete)
379
380
381func __action(action) -> void:
382 var undo_redo: UndoRedo = __Singletons.instance_of(__EditContext, self).undo_redo
383
384 match(action):
385 ACTIONS.DELETE:
386 var task = board_data.get_task(data_uuid)
387 for uuid in board_data.get_stages():
388 var tasks := board_data.get_stage(uuid).tasks
389 if data_uuid in tasks:
390 tasks.erase(data_uuid)
391 undo_redo.create_action("Delete task")
392 undo_redo.add_do_property(board_data.get_stage(uuid), &"tasks", tasks)
393 undo_redo.add_do_method(board_data.remove_task.bind(data_uuid, true))
394 undo_redo.add_undo_method(board_data.__add_task.bind(task, data_uuid))
395 undo_redo.add_undo_property(board_data.get_stage(uuid), &"tasks", board_data.get_stage(uuid).tasks)
396 undo_redo.add_undo_reference(task)
397 undo_redo.commit_action()
398 break
399
400 ACTIONS.DETAILS:
401 details.board_data = board_data
402 details.data_uuid = data_uuid
403 details.popup_centered_ratio_no_fullscreen(0.5)
404
405 ACTIONS.DUPLICATE:
406 var copy := __TaskData.new()
407 copy.from_json(board_data.get_task(data_uuid).to_json())
408 var copy_uuid := board_data.add_task(copy)
409 for uuid in board_data.get_stages():
410 var tasks := board_data.get_stage(uuid).tasks
411 if data_uuid in tasks:
412 tasks.insert(tasks.find(data_uuid), copy_uuid)
413 undo_redo.create_action("Duplicate task")
414 undo_redo.add_do_method(board_data.__add_task.bind(copy, copy_uuid))
415 undo_redo.add_do_property(board_data.get_stage(uuid), &"tasks", tasks)
416 undo_redo.add_undo_property(board_data.get_stage(uuid), &"tasks", board_data.get_stage(uuid).tasks)
417 undo_redo.add_undo_method(board_data.remove_task.bind(copy_uuid))
418 undo_redo.commit_action(false)
419
420 board_data.get_stage(uuid).tasks = tasks
421 break
422
423 ACTIONS.RENAME:
424 if context_menu.visible:
425 await context_menu.popup_hide
426 title_label.show_edit()
427
428
429func __set_title(value: String) -> void:
430 board_data.get_task(data_uuid).title = value
431
432
433func __on_category_button_pressed() -> void:
434 __category_menu.popup_at_local_position(category_button, Vector2(0, category_button.size.y))
435
436
437func __on_category_menu_uuid_selected(category_uuid) -> void:
438 var task = board_data.get_task(data_uuid)
439 task.category = category_uuid
addons/kanban_tasks/view/task/task.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://uywctdmttjfw
addons/kanban_tasks/view/task/task.tscn +0 -108
……@@ -1,108 +0,0 @@
1[gd_scene load_steps=8 format=3 uid="uid://ckqrwj5kxr6vl"]
2
3[ext_resource type="Script" uid="uid://uywctdmttjfw" path="res://addons/kanban_tasks/view/task/task.gd" id="1_dslv8"]
4[ext_resource type="Script" uid="uid://bj3jwa1phfj3t" path="res://addons/kanban_tasks/edit_label/edit_label.gd" id="2_iitpi"]
5[ext_resource type="Script" uid="uid://bau1py83aff06" path="res://addons/kanban_tasks/view/task/autosize_label.gd" id="3_1qkab"]
6[ext_resource type="PackedScene" uid="uid://bwi22eyrmeeet" path="res://addons/kanban_tasks/view/details/details.tscn" id="3_2ol5j"]
7[ext_resource type="PackedScene" uid="uid://dwjg5vyxx4g48" path="res://addons/kanban_tasks/view/details/step_holder.tscn" id="4_4e7a7"]
8[ext_resource type="Script" uid="uid://dyga42pgcqrsl" path="res://addons/kanban_tasks/expand_button/expand_button.gd" id="5_sgwao"]
9
10[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3iasq"]
11bg_color = Color(0.1, 0.1, 0.1, 0.6)
12border_width_left = 8
13
14[node name="Task" type="MarginContainer"]
15editor_description = "This container is needed because the panel style cannot be updated from a script on the panel container."
16custom_minimum_size = Vector2(150, 0)
17offset_right = 150.0
18offset_bottom = 50.0
19size_flags_horizontal = 3
20focus_mode = 2
21script = ExtResource("1_dslv8")
22
23[node name="Panel" type="PanelContainer" parent="."]
24unique_name_in_owner = true
25show_behind_parent = true
26layout_mode = 2
27size_flags_horizontal = 3
28mouse_filter = 2
29theme_override_styles/panel = SubResource("StyleBoxFlat_3iasq")
30
31[node name="HBoxContainer" type="HBoxContainer" parent="Panel"]
32layout_mode = 2
33size_flags_horizontal = 3
34size_flags_vertical = 3
35mouse_filter = 2
36theme_override_constants/separation = 0
37
38[node name="MarginContainer" type="MarginContainer" parent="Panel/HBoxContainer"]
39layout_mode = 2
40size_flags_horizontal = 3
41size_flags_vertical = 3
42mouse_filter = 2
43theme_override_constants/margin_left = 5
44theme_override_constants/margin_top = 5
45theme_override_constants/margin_right = 0
46theme_override_constants/margin_bottom = 5
47
48[node name="VBoxContainer" type="VBoxContainer" parent="Panel/HBoxContainer/MarginContainer"]
49layout_mode = 2
50alignment = 1
51
52[node name="HBoxContainer" type="HBoxContainer" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer"]
53layout_mode = 2
54
55[node name="CategoryButton" type="Button" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"]
56unique_name_in_owner = true
57layout_mode = 2
58focus_mode = 0
59
60[node name="Title" type="VBoxContainer" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer/HBoxContainer"]
61unique_name_in_owner = true
62custom_minimum_size = Vector2(0, 34.1)
63layout_mode = 2
64size_flags_horizontal = 3
65alignment = 1
66script = ExtResource("2_iitpi")
67
68[node name="Description" type="Label" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer"]
69unique_name_in_owner = true
70visible = false
71modulate = Color(1, 1, 1, 0.443137)
72layout_mode = 2
73size_flags_horizontal = 3
74autowrap_mode = 3
75text_overrun_behavior = 3
76script = ExtResource("3_1qkab")
77
78[node name="StepHolder" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer" instance=ExtResource("4_4e7a7")]
79unique_name_in_owner = true
80layout_mode = 2
81scrollable = false
82steps_can_be_removed = false
83steps_can_be_reordered = false
84steps_have_context_menu = false
85steps_focus_mode = null
86
87[node name="ExpandButton" type="Button" parent="Panel/HBoxContainer/MarginContainer/VBoxContainer"]
88unique_name_in_owner = true
89layout_mode = 2
90focus_mode = 0
91theme_type_variation = &"ExpandButton"
92flat = true
93icon_alignment = 1
94script = ExtResource("5_sgwao")
95expanded = false
96
97[node name="Edit" type="Button" parent="Panel/HBoxContainer"]
98unique_name_in_owner = true
99layout_mode = 2
100focus_mode = 0
101flat = true
102
103[node name="ContextMenu" type="PopupMenu" parent="."]
104unique_name_in_owner = true
105allow_search = false
106
107[node name="Details" parent="." instance=ExtResource("3_2ol5j")]
108unique_name_in_owner = true
addons/kanban_tasks/view/tooltip.gd +0 -47
……@@ -1,47 +0,0 @@
1@tool
2extends RichTextLabel
3
4
5@export var mimicked_paragraph_spacing_font_size: int = 6
6
7
8func _init() -> void:
9 bbcode_enabled = true
10 fit_content = true
11 custom_minimum_size.x = 500
12 resized.connect(__on_resized)
13
14
15func _notification(what) -> void:
16 match what:
17 NOTIFICATION_ENTER_TREE:
18 __take_over_label_style()
19
20
21func mimic_paragraphs() -> void:
22 var what_in_order: PackedStringArray = [
23 "[/p]\n[p]",
24 "[/p][p]",
25 "[p][/p]",
26 "[p]",
27 "[/p]",
28 ]
29 var forwhat = "\n[font_size=%s]\n[/font_size]\n" % mimicked_paragraph_spacing_font_size
30 var new_text := text
31 new_text = new_text.trim_prefix("[p]").trim_suffix("[/p]")
32 for what in what_in_order:
33 new_text = new_text.replace(what, forwhat)
34 new_text = new_text.trim_prefix("\n").trim_suffix("\n")
35 text = new_text
36
37
38func __take_over_label_style() -> void:
39 add_theme_stylebox_override(&"normal", get_theme_stylebox(&"normal", &"Label"))
40
41
42func __on_resized() -> void:
43 # Reduce width if unnecessary, as there is no line wraps
44 var stylebox = get_theme_stylebox(&"normal")
45 var required_width = get_content_width() + stylebox.content_margin_left + stylebox.content_margin_right
46 if required_width < custom_minimum_size.x:
47 custom_minimum_size.x = required_width
addons/kanban_tasks/view/tooltip.gd.uid +0 -1
……@@ -1 +0,0 @@
1uid://c2h3wagifru40
icon.svg.import +1 -1
……@@ -2,7 +2,7 @@
22
33importer="texture"
44type="CompressedTexture2D"
5uid="uid://bq34js3rsxb1o"
5uid="uid://dxr76wkx43rxd"
66path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
77metadata={
88"vram_texture": false