移除godot表插件
This commit is contained in:
parent
28f6cd4b85
commit
abba859cbf
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -13,4 +13,5 @@
|
|||
/DScript/DScript_Compiler_Test/obj
|
||||
/DScript/DScript_Runtime/Backups
|
||||
/DScript/DScript.sln.DotSettings.user
|
||||
**/.idea
|
||||
**/.idea
|
||||
**/~$*
|
BIN
DungeonShooting_Config/Weapon.xlsx
Normal file
BIN
DungeonShooting_Config/Weapon.xlsx
Normal file
Binary file not shown.
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.1.0-dev.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\game\ui\editorTools" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.1.0-dev.2">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\game\ui\editorTools" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.1.0-dev.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\game\ui\editorTools" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.1.0-dev.3">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\game\ui\editorTools" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.1.0-dev.1">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\game\ui\editorTools" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,11 +0,0 @@
|
|||
<Project Sdk="Godot.NET.Sdk/4.1.0-dev.3">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\game\ui\editorTools" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,310 +0,0 @@
|
|||
#============================================================
|
||||
# Data Util
|
||||
#============================================================
|
||||
# - datetime: 2022-12-21 21:19:10
|
||||
#============================================================
|
||||
## 数据工具
|
||||
##
|
||||
##用作全局获取数据使用
|
||||
class_name ScriptCommentMenu_DataUtil
|
||||
|
||||
|
||||
## 获取场景树 [SceneTree] 对象的 meta 数据作为单例数据,如果返回的数据为 [code]null[/code] 则会在下次继续调用这个
|
||||
##default 回调方法,直到返回的数据不为 [code]null[/code] 为止
|
||||
##[br]
|
||||
##[br][code]meta_key[/code] 数据key
|
||||
##[br][code]default[/code] 如果没有这个key,则默认返回的数据
|
||||
##[br][code]ignore_null[/code] 忽略 null 值。如果为 true,则在默认值为 null 的时候不记录到元数据,直到有数据为止
|
||||
static func get_meta_data(meta_key: StringName, default: Callable, ignore_null: bool = true):
|
||||
if Engine.has_meta(meta_key) and Engine.get_meta(meta_key) != null:
|
||||
return Engine.get_meta(meta_key)
|
||||
else:
|
||||
var value = default.call()
|
||||
if ignore_null:
|
||||
if value != null:
|
||||
set_meta_data(meta_key, value)
|
||||
else:
|
||||
set_meta_data(meta_key, value)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
## 设置数据
|
||||
##[br]
|
||||
##[br][code]meta_key[/code] 数据key
|
||||
##[br][code]value[/code] 设置的值
|
||||
static func set_meta_data(meta_key: StringName, value):
|
||||
Engine.set_meta(meta_key, value)
|
||||
|
||||
|
||||
## 是否有这个 key 的据
|
||||
static func has_meta_data(meta_key: StringName) -> bool:
|
||||
return Engine.has_meta(meta_key)
|
||||
|
||||
|
||||
## 移除数据
|
||||
static func remove_meta_data(meta_key: StringName) -> bool:
|
||||
if Engine.has_meta(meta_key):
|
||||
Engine.remove_meta(meta_key)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
## 移除所有meta数据
|
||||
static func clear_all_meta() -> void:
|
||||
for key in Engine.get_meta_list():
|
||||
Engine.remove_meta(key)
|
||||
|
||||
|
||||
## 获取 Dictionary 数据
|
||||
static func get_meta_dict_data(meta_key: StringName, default: Dictionary = {}) -> Dictionary:
|
||||
if Engine.has_meta(meta_key):
|
||||
return Engine.get_meta(meta_key)
|
||||
else:
|
||||
Engine.set_meta(meta_key, default)
|
||||
return default
|
||||
|
||||
|
||||
## 获取 Array 数据
|
||||
static func get_meta_array_data(meta_key: StringName, default: Array = []) -> Array:
|
||||
if Engine.has_meta(meta_key):
|
||||
return Engine.get_meta(meta_key)
|
||||
else:
|
||||
Engine.set_meta(meta_key, default)
|
||||
return default
|
||||
|
||||
|
||||
## 获取目标的默认数据,以目标对象作为基础存储数据
|
||||
static func get_object_data(object: Object, key: StringName, default: Callable ):
|
||||
if object.has_meta(key):
|
||||
return object.get_meta(key)
|
||||
else:
|
||||
var data = default.call()
|
||||
object.set_meta(key, data)
|
||||
return data
|
||||
|
||||
|
||||
## 获取标 [Dictionary] 类型数据
|
||||
static func get_object_dict_data(object: Object, key: StringName, default: Dictionary = {}) -> Dictionary:
|
||||
return get_object_data(object, key, func(): return default)
|
||||
|
||||
|
||||
class _ClassInfo:
|
||||
var _type : int = TYPE_NIL
|
||||
var _class_name : StringName = &""
|
||||
var _script : Script = null
|
||||
|
||||
func _to_string():
|
||||
return str({
|
||||
"_type": _type,
|
||||
"_class_name": _class_name,
|
||||
"_script": _script,
|
||||
})
|
||||
|
||||
|
||||
## 获取类的数据
|
||||
##[br]
|
||||
##[br][code]_class[/code] 类型。这个值可以是类名称,也可以是 [int] 类的数据型枚举的值。最大
|
||||
## [constant TYPE_MAX],最小 [constant TYPE_NIL]
|
||||
##[br][code]return[/code] 返回这个类的信息
|
||||
static func get_class_info(_class) -> _ClassInfo:
|
||||
var map = get_meta_dict_data("DataUtil_get_type_cache_data_for_array", {})
|
||||
if map.has(_class):
|
||||
return map[_class] as _ClassInfo
|
||||
|
||||
else:
|
||||
var type : int = TYPE_NIL
|
||||
var _class_name : StringName = &""
|
||||
var script = null
|
||||
if _class is Script:
|
||||
type = TYPE_OBJECT
|
||||
_class_name = _class.get_instance_base_type()
|
||||
script = _class
|
||||
elif _class is int and _class > 0 and _class < TYPE_MAX:
|
||||
type = _class
|
||||
_class = ScriptCommentMenu_ScriptUtil.get_type_name(_class)
|
||||
elif _class is Object:
|
||||
var _class_type_ = str(_class)
|
||||
if _class_type_.contains("GDScriptNativeClass"):
|
||||
var obj = _class.new()
|
||||
type = typeof(obj)
|
||||
_class_name = obj.get_class()
|
||||
else:
|
||||
type = TYPE_OBJECT
|
||||
_class_name = "Object"
|
||||
elif _class is String:
|
||||
if ScriptCommentMenu_ScriptUtil.is_base_data_type(_class):
|
||||
type = ScriptCommentMenu_ScriptUtil.get_type_of(_class)
|
||||
_class = ScriptCommentMenu_ScriptUtil.get_built_in_class(_class)
|
||||
else:
|
||||
type = TYPE_OBJECT
|
||||
|
||||
var data = _ClassInfo.new()
|
||||
data._type = type
|
||||
data._class_name = _class_name
|
||||
data._script = script
|
||||
map[_class] = data
|
||||
return data
|
||||
|
||||
|
||||
## 获取类型化数组
|
||||
##[br]
|
||||
##[br][code]_class[/code] 数据的类型。比如 [code]"Dictionary", Node, Sprite2D[/code] 等类名(基础数据类型需要加双引号),
|
||||
##或者自定义类名 Player,或者字符串形式的类名,或者 TYPE_INT, TYPE_DICTIONARY
|
||||
##[br][code]default[/code] 默认有哪些数据
|
||||
static func get_type_array(_class, default : Array = []) -> Array:
|
||||
var data : _ClassInfo = get_class_info(_class)
|
||||
# 返回类型化数组
|
||||
return Array(default, data._type, data._class_name, data._script )
|
||||
|
||||
|
||||
## 转为类型化数组
|
||||
static func to_type_array(_class, array: Array) -> Array:
|
||||
return get_type_array(_class, array)
|
||||
|
||||
|
||||
## 数组转为字典
|
||||
##
|
||||
##[codeblock]
|
||||
##var dict_data = ScriptCommentMenu_DataUtil.array_to_dictionary(
|
||||
## node_list,
|
||||
## func(node): return node.name, # key 键
|
||||
## func(node): return {}
|
||||
##)
|
||||
##[/codeblock]
|
||||
static func array_to_dictionary(
|
||||
list: Array,
|
||||
get_key: Callable = func(item): return item,
|
||||
get_value: Callable = func(item): return null
|
||||
) -> Dictionary:
|
||||
var data = {}
|
||||
var key
|
||||
var value
|
||||
for i in list:
|
||||
key = get_key.call(i)
|
||||
value = get_value.call(i)
|
||||
data[key] = value
|
||||
return data
|
||||
|
||||
|
||||
## 引用数据
|
||||
class RefData:
|
||||
var value
|
||||
|
||||
func get_value():
|
||||
return value
|
||||
|
||||
func _init(value) -> void:
|
||||
self.value = value
|
||||
|
||||
func _to_string():
|
||||
return str(value)
|
||||
|
||||
|
||||
## 获取引用数据。
|
||||
##[br]
|
||||
##[br][b]Note:[/b] 主要用在匿名函数里,以处理基本数据类型的值。因为匿名函数之外的基本数据类型的值
|
||||
##在匿名函数修改不会发生改变。
|
||||
static func get_ref_data(default) -> RefData:
|
||||
return RefData.new(default)
|
||||
|
||||
|
||||
## 获取字典的值,如果没有,则获取并设置默认值
|
||||
##[br]
|
||||
##[br][code]dict[/code] 获取的字典
|
||||
##[br][code]key[/code] key 键
|
||||
##[br][code]not_exists_set[/code] 没有则返回值设置这个值。这个回调方法返回要设置的数据
|
||||
static func get_value_or_set(dict: Dictionary, key, not_exists_set: Callable):
|
||||
if dict.has(key) and not typeof(dict[key]) == TYPE_NIL:
|
||||
return dict[key]
|
||||
else:
|
||||
dict[key] = not_exists_set.call()
|
||||
return dict[key]
|
||||
|
||||
|
||||
## 生成id
|
||||
static func generate_id(data_list: Array) -> StringName:
|
||||
var list = []
|
||||
for i in data_list:
|
||||
list.append(hash(i))
|
||||
return ",".join(list).sha1_text()
|
||||
|
||||
|
||||
## 如果不为空值结果值
|
||||
class NotNullValueChain:
|
||||
|
||||
func _init(value):
|
||||
set_meta("value", value)
|
||||
|
||||
func get_value(default = null):
|
||||
return get_meta("value", default)
|
||||
|
||||
func or_else(object, else_object: Callable) -> NotNullValueChain:
|
||||
return NotNullValueChain.new( object if object else else_object.call() )
|
||||
|
||||
## 返回结果不为空时,这个方法需要一个参数接收值
|
||||
func if_not_null(else_object: Callable, default = null) -> NotNullValueChain:
|
||||
var value = get_value()
|
||||
return NotNullValueChain.new( else_object.call(value) if value else default )
|
||||
|
||||
|
||||
## 如果对象不为 null 则调用。
|
||||
## 可以链式调用逐步执行功能
|
||||
##[codeblock]
|
||||
##func get_data(object: Object):
|
||||
## return ScriptCommentMenu_DataUtil.if_not_null(object, func():
|
||||
## return object.get_script()
|
||||
## ).or_else(func():
|
||||
## print("")
|
||||
## )
|
||||
##[/codeblock]
|
||||
static func if_not_null(object, else_object: Callable) -> NotNullValueChain:
|
||||
return NotNullValueChain.new((
|
||||
else_object.call() if object != null else object
|
||||
))
|
||||
|
||||
|
||||
## 获取正则
|
||||
static func get_regex(pattern: String) -> RegEx:
|
||||
var re = RegEx.new()
|
||||
re.compile(pattern)
|
||||
return re
|
||||
|
||||
|
||||
## 合并数据
|
||||
##[br]
|
||||
##[br][code]merge_target[/code] 合并到的目标
|
||||
##[br][code]data[/code] 要追加合并的数据
|
||||
##[br][return]return[/return] 返回合并后的数据
|
||||
static func merge(merge_target, data):
|
||||
if merge_target is Dictionary:
|
||||
merge_target.merge(data)
|
||||
return merge_target
|
||||
elif merge_target is Array or merge_target is String:
|
||||
merge_target += merge_target
|
||||
return merge_target
|
||||
else:
|
||||
assert(false, "错误的数据类型!只能合并 [Dictionary, Array, String] 中的一种!")
|
||||
|
||||
|
||||
## 获取一个唯一的数字 ID,从 0 始
|
||||
static func get_id() -> int:
|
||||
const KEY = "DataUtil_get_id"
|
||||
if Engine.has_meta(KEY):
|
||||
var id = Engine.get_meta(KEY)
|
||||
id += 1
|
||||
Engine.set_meta(KEY, id)
|
||||
return id
|
||||
else:
|
||||
var id = 0
|
||||
Engine.set_meta(KEY, id)
|
||||
return id
|
||||
|
||||
|
||||
## 列表转为集合hash值,这样即便列表顺序不一致他的值也是相同的
|
||||
static func as_set_hash(list: Array) -> int:
|
||||
var h : int = 0
|
||||
for i in list:
|
||||
h += hash(i)
|
||||
return h
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
[plugin]
|
||||
|
||||
name="script_comment_menu"
|
||||
description=""
|
||||
author="张学徒"
|
||||
version="1.2"
|
||||
script="plugin.gd"
|
|
@ -1,47 +0,0 @@
|
|||
#============================================================
|
||||
# Plugin
|
||||
#============================================================
|
||||
# - datetime: 2022-06-11 11:26:00
|
||||
# - datetime: 2022-07-17 14:54:39
|
||||
#============================================================
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
|
||||
var menu_button : MenuButton
|
||||
var util_add_menu := ScriptCommentMenuConstant.AddMenu.new()
|
||||
|
||||
var _sub_menus = [
|
||||
_ScriptMenu_Comments.new(),
|
||||
_ScriptMenu_Overrides.new(),
|
||||
]
|
||||
|
||||
|
||||
func _enter_tree():
|
||||
# 编辑器启动不超过 5 秒时
|
||||
if Time.get_ticks_msec() < 5000:
|
||||
await Engine.get_main_loop().create_timer(10).timeout
|
||||
_init_data.call_deferred()
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
if menu_button:
|
||||
menu_button.queue_free()
|
||||
for sub in _sub_menus:
|
||||
sub._uninstall()
|
||||
|
||||
|
||||
func _init_data():
|
||||
# 添加菜单按钮
|
||||
menu_button = MenuButton.new()
|
||||
menu_button.text = "代码工具"
|
||||
menu_button.switch_on_hover = true
|
||||
menu_button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||
util_add_menu.add_script_editor_menu(menu_button)
|
||||
|
||||
for sub in _sub_menus:
|
||||
sub.init_menu(menu_button)
|
||||
|
||||
|
||||
|
|
@ -1,401 +0,0 @@
|
|||
#============================================================
|
||||
# Scirpt Util
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 17:25:00
|
||||
#============================================================
|
||||
## 处理脚本的工具
|
||||
class_name ScriptCommentMenu_ScriptUtil
|
||||
|
||||
|
||||
const DATA_TYPE_TO_NAME = {
|
||||
TYPE_NIL: &"null",
|
||||
TYPE_BOOL: &"bool",
|
||||
TYPE_INT: &"int",
|
||||
TYPE_FLOAT: &"float",
|
||||
TYPE_STRING: &"String",
|
||||
TYPE_RECT2: &"Rect2",
|
||||
TYPE_VECTOR2: &"Vector2",
|
||||
TYPE_VECTOR2I: &"Vector2i",
|
||||
TYPE_VECTOR3: &"Vector3",
|
||||
TYPE_VECTOR3I: &"Vector3i",
|
||||
TYPE_TRANSFORM2D: &"Transform2D",
|
||||
TYPE_VECTOR4: &"Vector4",
|
||||
TYPE_VECTOR4I: &"Vector4i",
|
||||
TYPE_PLANE: &"Plane",
|
||||
TYPE_QUATERNION: &"Quaternion",
|
||||
TYPE_AABB: &"AABB",
|
||||
TYPE_BASIS: &"Basis",
|
||||
TYPE_TRANSFORM3D: &"Transform3D",
|
||||
TYPE_PROJECTION: &"Projection",
|
||||
TYPE_COLOR: &"Color",
|
||||
TYPE_STRING_NAME: &"StringName",
|
||||
TYPE_NODE_PATH: &"NodePath",
|
||||
TYPE_RID: &"RID",
|
||||
TYPE_OBJECT: &"Object",
|
||||
TYPE_CALLABLE: &"Callable",
|
||||
TYPE_SIGNAL: &"Signal",
|
||||
TYPE_DICTIONARY: &"Dictionary",
|
||||
TYPE_ARRAY: &"Array",
|
||||
TYPE_PACKED_BYTE_ARRAY: &"PackedByteArray",
|
||||
TYPE_PACKED_INT32_ARRAY: &"PackedInt32Array",
|
||||
TYPE_PACKED_INT64_ARRAY: &"PackedInt64Array",
|
||||
TYPE_PACKED_STRING_ARRAY: &"PackedStringArray",
|
||||
TYPE_PACKED_VECTOR2_ARRAY: &"PackedVector2Array",
|
||||
TYPE_PACKED_VECTOR3_ARRAY: &"PackedVector3Array",
|
||||
TYPE_PACKED_FLOAT32_ARRAY: &"PackedFloat32Array",
|
||||
TYPE_PACKED_FLOAT64_ARRAY: &"PackedFloat64Array",
|
||||
TYPE_PACKED_COLOR_ARRAY: &"PackedColorArray",
|
||||
}
|
||||
|
||||
const NAME_TO_DATA_TYPE = {
|
||||
&"null": TYPE_NIL,
|
||||
&"bool": TYPE_BOOL,
|
||||
&"int": TYPE_INT,
|
||||
&"float": TYPE_FLOAT,
|
||||
&"String": TYPE_STRING,
|
||||
&"Rect2": TYPE_RECT2,
|
||||
&"Vector2": TYPE_VECTOR2,
|
||||
&"Vector2i": TYPE_VECTOR2I,
|
||||
&"Vector3": TYPE_VECTOR3,
|
||||
&"Vector3i": TYPE_VECTOR3I,
|
||||
&"Transform2D": TYPE_TRANSFORM2D,
|
||||
&"Vector4": TYPE_VECTOR4,
|
||||
&"Vector4i": TYPE_VECTOR4I,
|
||||
&"Plane": TYPE_PLANE,
|
||||
&"Quaternion": TYPE_QUATERNION,
|
||||
&"AABB": TYPE_AABB,
|
||||
&"Basis": TYPE_BASIS,
|
||||
&"Transform3D": TYPE_TRANSFORM3D,
|
||||
&"Projection": TYPE_PROJECTION,
|
||||
&"Color": TYPE_COLOR,
|
||||
&"StringName": TYPE_STRING_NAME,
|
||||
&"NodePath": TYPE_NODE_PATH,
|
||||
&"RID": TYPE_RID,
|
||||
&"Object": TYPE_OBJECT,
|
||||
&"Callable": TYPE_CALLABLE,
|
||||
&"Signal": TYPE_SIGNAL,
|
||||
&"Dictionary": TYPE_DICTIONARY,
|
||||
&"Array": TYPE_ARRAY,
|
||||
&"PackedByteArray": TYPE_PACKED_BYTE_ARRAY,
|
||||
&"PackedInt32Array": TYPE_PACKED_INT32_ARRAY,
|
||||
&"PackedInt64Array": TYPE_PACKED_INT64_ARRAY,
|
||||
&"PackedStringArray": TYPE_PACKED_STRING_ARRAY,
|
||||
&"PackedVector2Array": TYPE_PACKED_VECTOR2_ARRAY,
|
||||
&"PackedVector3Array": TYPE_PACKED_VECTOR3_ARRAY,
|
||||
&"PackedFloat32Array": TYPE_PACKED_FLOAT32_ARRAY,
|
||||
&"PackedFloat64Array": TYPE_PACKED_FLOAT64_ARRAY,
|
||||
&"PackedColorArray": TYPE_PACKED_COLOR_ARRAY,
|
||||
}
|
||||
|
||||
|
||||
static func _get_script_data_cache(script: Script) -> Dictionary:
|
||||
return ScriptCommentMenu_DataUtil.get_meta_dict_data("ScriptCommentMenu_ScriptUtil__get_script_data_cache")
|
||||
|
||||
## 数据类型名称
|
||||
##[br][code]type[/code]: 数据类型枚举值
|
||||
##[br][code]return[/code]: 返回数据类型的字符串
|
||||
static func get_type_name(type: int) -> StringName:
|
||||
return DATA_TYPE_TO_NAME.get(type)
|
||||
|
||||
## 获取这个类名的类型
|
||||
static func get_type_of(_class_name: StringName) -> int:
|
||||
return NAME_TO_DATA_TYPE.get(_class_name, -1)
|
||||
|
||||
## 是否有这个类型的枚举
|
||||
static func has_type(type: int) -> bool:
|
||||
return DATA_TYPE_TO_NAME.has(type)
|
||||
|
||||
## 是否是基础数据类型
|
||||
static func is_base_data_type(_class_name: StringName) -> bool:
|
||||
return NAME_TO_DATA_TYPE.has(_class_name)
|
||||
|
||||
## 获取属性列表
|
||||
##[br]
|
||||
##[br]返回类似如下格式的数据
|
||||
##[codeblock]
|
||||
##{
|
||||
## "name": "RefCounted",
|
||||
## "class_name": &"",
|
||||
## "type": 0,
|
||||
## "hint": 0,
|
||||
## "hint_string": "",
|
||||
## "usage": 128
|
||||
##}
|
||||
##[/codeblock]
|
||||
static func get_property_data_list(script: Script) -> Array[Dictionary]:
|
||||
if is_instance_valid(script):
|
||||
return script.get_script_property_list()
|
||||
return Array([], TYPE_DICTIONARY, "Dictionary", null)
|
||||
|
||||
## 获取方法列表
|
||||
static func get_method_data_list(script: Script) -> Array[Dictionary]:
|
||||
if is_instance_valid(script):
|
||||
return script.get_script_method_list()
|
||||
ScriptCommentMenu_DataUtil.get_type_array("int")
|
||||
return Array([], TYPE_DICTIONARY, "Dictionary", null)
|
||||
|
||||
|
||||
## 获取方法的参数列表数据
|
||||
static func get_method_arguments_list(script: Script, method_name: StringName) -> Array[Dictionary]:
|
||||
var data = get_method_data(script, method_name)
|
||||
if data:
|
||||
return data.get("args", ScriptCommentMenu_DataUtil.get_type_array("Dictionary"))
|
||||
return ScriptCommentMenu_DataUtil.get_type_array("Dictionary")
|
||||
|
||||
|
||||
## 获取信号列表
|
||||
static func get_signal_data_list(script: Script) -> Array[Dictionary]:
|
||||
if is_instance_valid(script):
|
||||
return script.get_script_signal_list()
|
||||
return Array([], TYPE_DICTIONARY, "Dictionary", null)
|
||||
|
||||
## 获取这个属性名称数据
|
||||
static func get_property_data(script: Script, property: StringName) -> Dictionary:
|
||||
var data = _get_script_data_cache(script)
|
||||
var p_cache_data : Dictionary = ScriptCommentMenu_DataUtil.get_value_or_set(data, "propery_data_cache", func():
|
||||
var property_data : Dictionary = {}
|
||||
for i in script.get_script_property_list():
|
||||
property_data[i['name']] = i
|
||||
return property_data
|
||||
)
|
||||
return p_cache_data.get(property, {})
|
||||
|
||||
## 获取这个名称的方法的数据
|
||||
static func get_method_data(script: Script, method_name: StringName) -> Dictionary:
|
||||
var data = _get_script_data_cache(script)
|
||||
var m_cache_data : Dictionary = ScriptCommentMenu_DataUtil.get_value_or_set(data, "method_data_cache", func():
|
||||
var method_data : Dictionary = {}
|
||||
for i in script.get_script_method_list():
|
||||
method_data[i['name']]=i
|
||||
return method_data
|
||||
)
|
||||
return m_cache_data.get(method_name, {})
|
||||
|
||||
## 获取这个名称的信号的数据
|
||||
static func get_signal_data(script: Script, signal_name: StringName):
|
||||
var data = _get_script_data_cache(script)
|
||||
var s_cache_data : Dictionary = ScriptCommentMenu_DataUtil.get_value_or_set(data, "script_data_cache", func():
|
||||
var signal_data : Dictionary = {}
|
||||
for i in script.get_script_signal_list():
|
||||
signal_data[i['name']]=i
|
||||
return signal_data
|
||||
)
|
||||
return s_cache_data.get(signal_name, {})
|
||||
|
||||
|
||||
## 获取方法数据
|
||||
## [br]
|
||||
## [br][code]script[/code]: 脚本
|
||||
## [br][code]method[/code]: 要获取的方法数据的方法名
|
||||
## [br]
|
||||
## [br][code]return[/code]: 返回脚本的数据信息。
|
||||
## 包括的 key 有 [code]name[/code], [code]args[/code], [code]default_args[/code]
|
||||
## , [code]flags[/code], [code]return[/code], [code]id[/code]
|
||||
func find_method_data(script: Script, method: String) -> Dictionary:
|
||||
var method_data = script.get_script_method_list()
|
||||
for m in method_data:
|
||||
if m['name'] == method:
|
||||
return m
|
||||
return {}
|
||||
|
||||
|
||||
## 获取扩展脚本链(扩展的所有脚本)
|
||||
##[br]
|
||||
##[br][code]script[/code] Object 对象或脚本
|
||||
##[br][code]return[/code] 返回继承的脚本路径列表
|
||||
static func get_extends_link(script: Script) -> PackedStringArray:
|
||||
var list := PackedStringArray()
|
||||
while script:
|
||||
if FileAccess.file_exists(script.resource_path):
|
||||
list.push_back(script.resource_path)
|
||||
script = script.get_base_script()
|
||||
return list
|
||||
|
||||
|
||||
## 获取基础类型继承链类列表
|
||||
##[br]
|
||||
##[br][code]_class[/code] 基础类型类名
|
||||
##[br][code]return[/code] 返回基础的类名列表
|
||||
static func get_extends_link_base(_class) -> PackedStringArray:
|
||||
if _class is Script:
|
||||
_class = _class.get_instance_base_type()
|
||||
elif _class is Object:
|
||||
_class = _class.get_class()
|
||||
|
||||
var c = _class
|
||||
var list = []
|
||||
while c != "":
|
||||
list.append(c)
|
||||
c = ClassDB.get_parent_class(c)
|
||||
return PackedStringArray(list)
|
||||
|
||||
|
||||
## 生成方法代码
|
||||
##[br]
|
||||
##[br][code]method_data[/code] 方法数据
|
||||
##[br][code]return[/code] 返回生成的代码
|
||||
static func generate_method_code(method_data: Dictionary) -> String:
|
||||
var temp := method_data.duplicate(true)
|
||||
var args := ""
|
||||
for i in temp['args']:
|
||||
var arg_name = i['name']
|
||||
var arg_type = ( get_type_name(i['type']) if i['type'] != TYPE_NIL else "")
|
||||
if arg_type.strip_edges() == "":
|
||||
arg_type = str(i['class_name'])
|
||||
if arg_type.strip_edges() != "":
|
||||
arg_type = ": " + arg_type
|
||||
args += "%s%s, " % [arg_name, arg_type]
|
||||
temp['args'] = args.trim_suffix(", ")
|
||||
if temp['return']['type'] != TYPE_NIL:
|
||||
temp['return_type'] = get_type_name(temp['return']['type'])
|
||||
|
||||
if temp.has('return_type') and temp['return_type'] != "":
|
||||
temp['return_type'] = " -> " + str(temp['return_type'])
|
||||
temp['return_sentence'] = "pass\n\treturn super." + temp['name'] + "()"
|
||||
else:
|
||||
temp['return_type'] = ""
|
||||
temp['return_sentence'] = "pass"
|
||||
|
||||
return "func {name}({args}){return_type}:\n\t{return_sentence}\n".format(temp)
|
||||
|
||||
|
||||
## 获取对象的脚本
|
||||
static func get_object_script(object: Object) -> Script:
|
||||
if object == null:
|
||||
return null
|
||||
if object is Script:
|
||||
return object
|
||||
return object.get_script() as Script
|
||||
|
||||
|
||||
## 对象是否是 tool 状态
|
||||
##[br]
|
||||
##[br][code]object[/code] 返回这个对象的脚本是否是开启 tool 的状态
|
||||
static func is_tool(object: Object) -> bool:
|
||||
var script = get_object_script(object)
|
||||
return script.is_tool() if script else false
|
||||
|
||||
|
||||
## 获取对象的脚本路径,如果不存在脚本,则返回空的字符串
|
||||
static func get_object_script_path(object: Object) -> String:
|
||||
var script = get_object_script(object)
|
||||
return script.resource_path if script else ""
|
||||
|
||||
|
||||
## 获取这个对象的这个方法的信息
|
||||
##[br]
|
||||
##[br][code]object[/code] 对象
|
||||
##[br][code]method_name[/code] 方法名
|
||||
##[br][code]return[/code] 返回方法的信息
|
||||
static func get_object_method_data(object: Object, method_name: StringName) -> Dictionary:
|
||||
if not is_instance_valid(object):
|
||||
return {}
|
||||
var script = get_object_script(object)
|
||||
if script:
|
||||
return get_method_data(script, method_name)
|
||||
return {}
|
||||
|
||||
|
||||
## 获取这个信号的数据
|
||||
static func get_object_signal_data(object: Object, signal_name: StringName) -> Dictionary:
|
||||
if not is_instance_valid(object):
|
||||
return {}
|
||||
var script = get_object_script(object)
|
||||
if script:
|
||||
return get_signal_data(script, signal_name)
|
||||
return {}
|
||||
|
||||
|
||||
## 获取对象的属性数据
|
||||
static func get_object_property_data(object: Object, proprety_name: StringName) -> Dictionary:
|
||||
if not is_instance_valid(object):
|
||||
return {}
|
||||
var script = get_object_script(object)
|
||||
if script:
|
||||
return get_property_data(script, proprety_name)
|
||||
return {}
|
||||
|
||||
|
||||
## 获取内置类名称转为对象。比如将 "Node" 字符串转为 [Node] 这种 GDScriptNativeClass 类型数据
|
||||
##[br]
|
||||
##[br][code]_class[/code] 类名称
|
||||
static func get_built_in_class (_class: StringName):
|
||||
if not ClassDB.class_exists(_class):
|
||||
return null
|
||||
var _class_db = ScriptCommentMenu_DataUtil.get_meta_dict_data("ScriptCommentMenu_ScriptUtil_get_built_in_class")
|
||||
return ScriptCommentMenu_DataUtil.get_value_or_set(_class_db, _class, func():
|
||||
var script = GDScript.new()
|
||||
script.source_code = "var type = " + _class
|
||||
if script.reload() == OK:
|
||||
var obj = script.new()
|
||||
_class_db[_class] = obj.type
|
||||
return _class_db[_class]
|
||||
else:
|
||||
push_error("错误的类名:", _class)
|
||||
return null
|
||||
)
|
||||
|
||||
|
||||
## 根据类名返回类对象
|
||||
static func get_script_class(_class: StringName):
|
||||
if ClassDB.class_exists(_class):
|
||||
return null
|
||||
var _class_db = ScriptCommentMenu_DataUtil.get_meta_dict_data("ScriptCommentMenu_ScriptUtil_get_script_class")
|
||||
return ScriptCommentMenu_DataUtil.get_value_or_set(_class_db, _class, func():
|
||||
var script = GDScript.new()
|
||||
script.source_code = "var type = " + _class
|
||||
if script.reload() == OK:
|
||||
var obj = script.new()
|
||||
_class_db[_class] = obj.type
|
||||
return _class_db[_class]
|
||||
else:
|
||||
push_error("错误的类名:", _class)
|
||||
return null
|
||||
)
|
||||
|
||||
|
||||
## 创建脚本
|
||||
static func create_script(source_code: String) -> GDScript:
|
||||
var data = ScriptCommentMenu_DataUtil.get_meta_dict_data("ScriptCommentMenu_ScriptUtil_create_script")
|
||||
return ScriptCommentMenu_DataUtil.get_value_or_set(data, source_code.sha256_text(), func():
|
||||
var script := GDScript.new()
|
||||
script.source_code = source_code
|
||||
script.reload()
|
||||
return script
|
||||
)
|
||||
|
||||
|
||||
## 获取这个类的场景。这个场景的位置和名称需要和脚本一致,只有后缀名不一样。这个类不能是内部类
|
||||
static func get_script_scene(script: GDScript) -> PackedScene:
|
||||
var data = ScriptCommentMenu_DataUtil.get_meta_dict_data("ScriptCommentMenu_ScriptUtil_get_script_scene")
|
||||
if data.has(script):
|
||||
return data[script]
|
||||
else:
|
||||
var path := script.resource_path
|
||||
if path == "":
|
||||
return null
|
||||
|
||||
var ext := path.get_extension()
|
||||
var file = path.substr(0, len(path) - len(ext))
|
||||
|
||||
var scene: PackedScene
|
||||
if FileAccess.file_exists(file + "tscn"):
|
||||
scene = ResourceLoader.load(file + "tscn", "PackedScene") as PackedScene
|
||||
elif FileAccess.file_exists(file + "scn"):
|
||||
scene = ResourceLoader.load(file + "scn", "PackedScene") as PackedScene
|
||||
else:
|
||||
printerr("这个类目录下没有相同名称的场景文件!")
|
||||
return null
|
||||
data[script] = scene
|
||||
return scene
|
||||
|
||||
|
||||
## 获取对象的类。如果是自定义类返回 [GDScript] 类;如果是内置类,则返回 [GDScriptNativeClass] 类
|
||||
static func get_object_class(object: Object):
|
||||
if object:
|
||||
if object is Script:
|
||||
return object
|
||||
if object.get_script() != null:
|
||||
return object.get_script()
|
||||
return get_built_in_class (object.get_class())
|
||||
return &""
|
|
@ -1,88 +0,0 @@
|
|||
#============================================================
|
||||
# @sub Tem
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 16:32:29
|
||||
#============================================================
|
||||
class_name _ScriptMenu_SubItem
|
||||
|
||||
|
||||
const MenuItemBuilder := ScriptCommentMenuConstant.MenuItemBuilder
|
||||
|
||||
|
||||
var _editor_plugin = EditorPlugin.new()
|
||||
var _util_script : ScriptCommentMenu_ScriptUtil
|
||||
var _util_script_editor : ScriptCommentMenuConstant.ScriptEditorUtil
|
||||
|
||||
|
||||
#============================================================
|
||||
# Set/Get
|
||||
#============================================================
|
||||
func get_editor_interface() -> EditorInterface:
|
||||
return _editor_plugin.get_editor_interface()
|
||||
|
||||
func get_script_editor_util():
|
||||
return _util_script_editor
|
||||
|
||||
func get_script_util() -> ScriptCommentMenu_ScriptUtil:
|
||||
return _util_script
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
## 外部调用初始化菜单
|
||||
##[br]
|
||||
##[br][code]menu_button[/code] 菜单按钮
|
||||
func init_menu(menu_button: MenuButton) -> void:
|
||||
if not menu_button.has_meta("IsInit"):
|
||||
_util_script_editor = ScriptCommentMenuConstant.ScriptEditorUtil.new()
|
||||
_util_script = ScriptCommentMenu_ScriptUtil.new()
|
||||
menu_button.set_meta("IsInit", {
|
||||
"_util_script_editor": _util_script_editor,
|
||||
"_util_script": _util_script,
|
||||
})
|
||||
|
||||
var data : Dictionary = menu_button.get_meta("IsInit")
|
||||
for property in data:
|
||||
var value = data[property]
|
||||
set(property, value)
|
||||
|
||||
_init_menu(menu_button)
|
||||
|
||||
|
||||
## 添加分隔符
|
||||
func add_separator(menu_button: MenuButton):
|
||||
(MenuItemBuilder.instance()
|
||||
.set_menu_by_menu_button(menu_button)
|
||||
.add_separator()
|
||||
.build()
|
||||
)
|
||||
|
||||
## 添加菜单
|
||||
func add_menu_item(menu_button: MenuButton, name: String, key_map: Dictionary, callable: Callable):
|
||||
# 添加菜单
|
||||
(MenuItemBuilder.instance()
|
||||
.set_menu(menu_button.get_popup())
|
||||
.set_item_name(name)
|
||||
.set_connect(callable)
|
||||
.set_key(key_map.get("key", false))
|
||||
.set_ctrl(key_map.get("ctrl", false))
|
||||
.set_shift(key_map.get("shift", false))
|
||||
.set_alt(key_map.get("alt", false))
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
|
||||
## 重写方法,初始化菜单
|
||||
##[br]
|
||||
##[br][code]menu_button[/code] 菜单按钮
|
||||
func _init_menu(menu_button: MenuButton) -> void:
|
||||
pass
|
||||
|
||||
|
||||
## 卸载子项
|
||||
func _uninstall():
|
||||
pass
|
||||
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#============================================================
|
||||
# Comment
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 14:49:15
|
||||
#============================================================
|
||||
## 脚本注释
|
||||
class_name _ScriptMenu_Comments
|
||||
extends _ScriptMenu_SubItem
|
||||
|
||||
|
||||
const SEPARATE_LENGTH = 60
|
||||
|
||||
|
||||
var util_script_editor := ScriptCommentMenuConstant.ScriptEditorUtil.new()
|
||||
var util_script := ScriptCommentMenu_ScriptUtil.new()
|
||||
|
||||
var regex = RegEx.new()
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _init():
|
||||
var pattern = "(?<indent>\\s*)(static\\s+)?func\\s+(?<method>[^\\(]+)"
|
||||
regex.compile(pattern)
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
#(override)
|
||||
func _init_menu(menu_button: MenuButton):
|
||||
# 设置添加菜单项
|
||||
var menu : PopupMenu = menu_button.get_popup()
|
||||
add_menu_item(menu_button, "脚本注释", {}, _script_comment)
|
||||
add_separator(menu_button)
|
||||
add_menu_item(menu_button, "方法注释", {
|
||||
"key": KEY_C,
|
||||
"ctrl": true,
|
||||
"shift": true,
|
||||
}, _func_comment)
|
||||
add_menu_item(menu_button, "类别分隔", {
|
||||
"key": KEY_SLASH,
|
||||
"ctrl": true,
|
||||
"shift": true,
|
||||
}, _category_comment)
|
||||
|
||||
|
||||
#============================================================
|
||||
# 功能
|
||||
#============================================================
|
||||
## 方法注释
|
||||
func _func_comment():
|
||||
var text_edit : TextEdit = util_script_editor.get_current_code_editor()
|
||||
var line : int = text_edit.get_caret_line()
|
||||
|
||||
for i in range(line, text_edit.get_line_count()):
|
||||
var line_code : String = text_edit.get_line(i)
|
||||
var result = regex.search(line_code)
|
||||
if result:
|
||||
var method = result.get_string("method").strip_edges()
|
||||
printt(method
|
||||
, util_script_editor.get_current_script()
|
||||
, util_script_editor.get_current_script().resource_path
|
||||
)
|
||||
|
||||
var indent = result.get_string("indent")
|
||||
var data = util_script.find_method_data(util_script_editor.get_current_script(), method)
|
||||
if data.size() == 0:
|
||||
printerr('没有找到', method,'方法的数据,脚本是否还未保存?')
|
||||
return
|
||||
|
||||
var code : String = "## %s\n" % data['name']
|
||||
if data['args'].size() > 0:
|
||||
code += (indent + "##[br]\n")
|
||||
for arg in data['args']:
|
||||
code += (indent + "##[br][code]%s[/code] \n" % arg['name'])
|
||||
if data['return']['type'] != TYPE_NIL:
|
||||
code += (indent + "##[br][code]return[/code] ")
|
||||
code = code.trim_suffix("\n")
|
||||
util_script_editor.insert_code_current_pos(code, true)
|
||||
break
|
||||
|
||||
## 脚本注释
|
||||
func _script_comment():
|
||||
var script = util_script_editor.get_current_script()
|
||||
if script == null:
|
||||
return
|
||||
|
||||
var separa = "=".repeat(SEPARATE_LENGTH)
|
||||
|
||||
# 脚本名
|
||||
var script_name = script.resource_path.get_file().get_basename().capitalize()
|
||||
# 时间
|
||||
var datetime = Time.get_datetime_dict_from_system()
|
||||
var datetime_str = "%02d-%02d-%02d %02d:%02d:%02d" % [
|
||||
datetime['year'], datetime['month'], datetime['day'],
|
||||
datetime['hour'], datetime['minute'], datetime['second'],
|
||||
]
|
||||
|
||||
var code = """#{sep}
|
||||
# {name}
|
||||
#{sep}
|
||||
# - author: zhangxuetu
|
||||
# - datetime: {datetime}
|
||||
# - version: 4.0
|
||||
#{sep}
|
||||
""".format({
|
||||
"sep": separa,
|
||||
"name": script_name,
|
||||
"datetime": datetime_str,
|
||||
})
|
||||
# 插入到顶部
|
||||
var textedit = util_script_editor.get_current_code_editor()
|
||||
textedit.set_caret_line(0)
|
||||
textedit.set_caret_column(0)
|
||||
textedit.insert_text_at_caret(code)
|
||||
|
||||
|
||||
## 类别分隔
|
||||
func _category_comment():
|
||||
var separa = "=".repeat(SEPARATE_LENGTH)
|
||||
var code = """#{sep}
|
||||
#
|
||||
#{sep}""".format({
|
||||
"sep": separa,
|
||||
})
|
||||
|
||||
var textedit = util_script_editor.get_current_code_editor()
|
||||
textedit.set_caret_column(0)
|
||||
textedit.insert_text_at_caret(code)
|
||||
textedit.set_caret_line(textedit.get_caret_line() - 1)
|
||||
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#============================================================
|
||||
# Dialog
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 15:49:30
|
||||
#============================================================
|
||||
@tool
|
||||
extends Control
|
||||
|
||||
|
||||
signal selected_method(method_names : Array)
|
||||
|
||||
|
||||
const SCRIPT_METHOD_LIST_SCRIPT = preload("res://addons/script_comment_menu/sub_item/override/scene/script_method_list.gd")
|
||||
const CHECK_LABEL = preload("res://addons/script_comment_menu/sub_item/override/scene/check_label.gd")
|
||||
|
||||
|
||||
@onready var confirmation_dialog = find_child("ConfirmationDialog")
|
||||
@onready var script_method_list = find_child("ScriptMethodList") as SCRIPT_METHOD_LIST_SCRIPT
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
## 显示弹窗
|
||||
func show_popup(script: Script):
|
||||
confirmation_dialog.popup_centered_ratio(0.6)
|
||||
script_method_list.update_data(script)
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
func _ready():
|
||||
confirmation_dialog.confirmed.connect(_confirmed)
|
||||
if not Engine.is_editor_hint():
|
||||
confirmation_dialog.popup_centered()
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
func _confirmed():
|
||||
var selected_items = script_method_list.get_selected_items()
|
||||
|
||||
var method_list = {}
|
||||
for item in selected_items:
|
||||
item = item as CHECK_LABEL
|
||||
var method_name : String = item.text
|
||||
method_list[method_name] = null
|
||||
item.selected = false
|
||||
selected_method.emit(method_list.keys())
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://cnx2ri54w1dpr"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/script_comment_menu/sub_item/override/dialog.gd" id="1_isfs8"]
|
||||
[ext_resource type="PackedScene" uid="uid://btcf0syabq2et" path="res://addons/script_comment_menu/sub_item/override/scene/script_method_list.tscn" id="2_lxp0b"]
|
||||
|
||||
[node name="Dialog" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_isfs8")
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||
dialog_close_on_escape = false
|
||||
|
||||
[node name="ScriptMethodList" parent="ConfirmationDialog" instance=ExtResource("2_lxp0b")]
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_right = 636.0
|
||||
offset_bottom = 342.0
|
|
@ -1,106 +0,0 @@
|
|||
#============================================================
|
||||
# Override
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 15:53:56
|
||||
#============================================================
|
||||
|
||||
## 重写
|
||||
class_name _ScriptMenu_Overrides
|
||||
extends _ScriptMenu_SubItem
|
||||
|
||||
|
||||
const DIALOG_SCRIPT = preload("dialog.gd")
|
||||
const DIALOG_SCENE = preload("dialog.tscn")
|
||||
|
||||
|
||||
var dialog = DIALOG_SCENE.instantiate() as DIALOG_SCRIPT
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
#(override)
|
||||
func _init_menu(menu_button: MenuButton):
|
||||
# 添加弹窗
|
||||
dialog.selected_method.connect(_selected_method)
|
||||
get_editor_interface().get_base_control().add_child(dialog)
|
||||
dialog.theme = get_editor_interface().get_base_control().theme
|
||||
# 添加菜单
|
||||
add_separator(menu_button)
|
||||
add_menu_item(menu_button, "重写方法", {
|
||||
"key": KEY_O,
|
||||
"ctrl": true,
|
||||
"shift": true,
|
||||
}, _show_popup)
|
||||
|
||||
|
||||
#(override)
|
||||
func _uninstall():
|
||||
super._uninstall()
|
||||
dialog.queue_free()
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
## 显示弹窗
|
||||
func _show_popup():
|
||||
var script = get_script_editor_util().get_current_script() as Script
|
||||
dialog.show_popup(script)
|
||||
|
||||
|
||||
const FORMAT = """
|
||||
#(override)
|
||||
func {method_name}({arguments}){return_type}:
|
||||
{return_value}super.{method_name}({parameters})
|
||||
|
||||
"""
|
||||
|
||||
func _selected_method(method_names : Array):
|
||||
|
||||
var text_edit = get_script_editor_util().get_current_code_editor() as TextEdit
|
||||
var script = get_script_editor_util().get_current_script() as Script
|
||||
|
||||
var code : String = ""
|
||||
|
||||
var added = {}
|
||||
for method_data in script.get_script_method_list():
|
||||
if added.has(method_data['name']) or not method_data['name'] in method_names:
|
||||
continue
|
||||
|
||||
added[method_data['name']] = null
|
||||
|
||||
var method_name = method_data['name']
|
||||
var method_type = method_data.get("type", 0)
|
||||
var method_args = method_data['args'] as Array
|
||||
var method_return = method_data['return']
|
||||
|
||||
# 参数列表
|
||||
var arguments : String = ", ".join(method_args.map(func(arg): return arg['name']))
|
||||
arguments = arguments.strip_edges().trim_suffix(",")
|
||||
|
||||
# 类型
|
||||
var return_type : String = ScriptCommentMenu_ScriptUtil.get_type_name(method_type)
|
||||
var return_value : String = ""
|
||||
if return_type == "null":
|
||||
return_type = ""
|
||||
if return_type != "":
|
||||
return_type = " -> " + return_type
|
||||
return_value = "return "
|
||||
|
||||
code += FORMAT.format({
|
||||
"method_name": method_name,
|
||||
"method_type": ScriptCommentMenu_ScriptUtil.get_type_name(method_type),
|
||||
"return_type": return_type,
|
||||
"return_value": "",
|
||||
"arguments": arguments,
|
||||
"parameters": arguments,
|
||||
})
|
||||
|
||||
text_edit.set_caret_column(0)
|
||||
text_edit.insert_text_at_caret(code)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
#============================================================
|
||||
# Check check_box
|
||||
#============================================================
|
||||
# - datetime: 2022-07-16 22:30:22
|
||||
#============================================================
|
||||
@tool
|
||||
extends HBoxContainer
|
||||
|
||||
|
||||
signal pressed
|
||||
|
||||
|
||||
@export
|
||||
var text : String = "":
|
||||
set(value):
|
||||
text = value
|
||||
if check_box == null:
|
||||
await ready
|
||||
check_box.text = value
|
||||
get:
|
||||
return check_box.text
|
||||
|
||||
@export
|
||||
var selected : bool = false :
|
||||
set(value):
|
||||
selected = value
|
||||
if check_box == null:
|
||||
await ready
|
||||
check_box.button_pressed = value
|
||||
get:
|
||||
return check_box.button_pressed
|
||||
|
||||
@export
|
||||
var color : Color = Color.WHITE :
|
||||
set(value):
|
||||
color = value
|
||||
if check_box == null:
|
||||
await ready
|
||||
check_box.modulate = color
|
||||
|
||||
|
||||
@onready var check_box = $CheckBox as CheckBox
|
||||
|
||||
|
||||
func _ready():
|
||||
check_box.pressed.connect(_pressed)
|
||||
|
||||
|
||||
func _pressed():
|
||||
pressed.emit()
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://bvoxy56c0um5v"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/script_comment_menu/sub_item/override/scene/check_label.gd" id="1_7qjn7"]
|
||||
|
||||
[node name="CheckLabel" type="HBoxContainer"]
|
||||
offset_right = 484.0
|
||||
offset_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
script = ExtResource("1_7qjn7")
|
||||
|
||||
[node name="CheckBox" type="CheckBox" parent="."]
|
||||
offset_right = 484.0
|
||||
offset_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
|
@ -1,88 +0,0 @@
|
|||
#============================================================
|
||||
# Item List
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 15:02:44
|
||||
#============================================================
|
||||
@tool
|
||||
extends VBoxContainer
|
||||
|
||||
|
||||
const CHECK_LABEL_SCENE = preload("check_label.tscn")
|
||||
const CHECK_LABEL_SCRIPT = preload("check_label.gd")
|
||||
|
||||
|
||||
var last_press_check : CHECK_LABEL_SCRIPT
|
||||
|
||||
|
||||
## 添加 Item
|
||||
##[br]
|
||||
##[br][code]label[/code]
|
||||
##[br][code]color[/code]
|
||||
##[br]
|
||||
##[br][code]return[/code]
|
||||
func add_item(label: String, color : Color = Color.WHITE) -> CHECK_LABEL_SCRIPT:
|
||||
var check_label = CHECK_LABEL_SCENE.instantiate()
|
||||
add_child(check_label)
|
||||
check_label.text = label
|
||||
check_label.color = color
|
||||
# 上一次点击的对象
|
||||
check_label.pressed.connect(_pressed.bind(check_label))
|
||||
return check_label
|
||||
|
||||
|
||||
## 添加组别标签
|
||||
##[br]
|
||||
##[br][code]text[/code] 标签名
|
||||
func add_label(text: String):
|
||||
var space = Control.new()
|
||||
space.custom_minimum_size.y = 4
|
||||
add_child(space)
|
||||
var panel = Panel.new()
|
||||
panel.custom_minimum_size.y = 1
|
||||
add_child(panel)
|
||||
var label = Label.new()
|
||||
label.text = text
|
||||
add_child(label)
|
||||
|
||||
|
||||
## 获取所有项
|
||||
func get_all_item() -> Array:
|
||||
var list = []
|
||||
for child in get_children():
|
||||
if child is CHECK_LABEL_SCRIPT:
|
||||
list.append(child)
|
||||
return list
|
||||
|
||||
|
||||
## 获取选中的项
|
||||
func get_selected_items() -> Array:
|
||||
var list = []
|
||||
for item in get_all_item():
|
||||
item = item as CHECK_LABEL_SCRIPT
|
||||
if item.selected:
|
||||
list.append(item)
|
||||
return list
|
||||
|
||||
## 清除所有
|
||||
func clear():
|
||||
for child in get_children():
|
||||
child.queue_free()
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
func _pressed(check: CHECK_LABEL_SCRIPT):
|
||||
# 如果是按着 Shift 键
|
||||
var all_item = get_all_item()
|
||||
if Input.is_key_pressed(KEY_SHIFT):
|
||||
if last_press_check != check:
|
||||
var last_index = all_item.find(last_press_check)
|
||||
var curr_index = all_item.find(check)
|
||||
var selected = check.selected
|
||||
for i in range(last_index, curr_index, sign(curr_index - last_index)):
|
||||
all_item[i].selected = selected
|
||||
|
||||
last_press_check = check
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://dmiw7e671j5ox"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/script_comment_menu/sub_item/override/scene/item_list.gd" id="1_of1bk"]
|
||||
|
||||
[node name="ItemList" type="VBoxContainer"]
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 600.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_of1bk")
|
|
@ -1,69 +0,0 @@
|
|||
#============================================================
|
||||
# Script Method List
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 15:22:58
|
||||
#============================================================
|
||||
@tool
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
const ITEM_LIST_SCRIPT = preload("item_list.gd")
|
||||
|
||||
|
||||
@onready var item_list = $ScrollContainer/ItemList as ITEM_LIST_SCRIPT
|
||||
@onready var scroll_container = $ScrollContainer
|
||||
|
||||
|
||||
## 获取所有选中的项
|
||||
func get_selected_items() -> Array:
|
||||
return item_list.get_selected_items()
|
||||
|
||||
|
||||
## 更新数据
|
||||
##[br]
|
||||
##[br][code]script[/code] 脚本
|
||||
func update_data(script: Script):
|
||||
# 获取脚本的继承的所有脚本类
|
||||
var scripts = []
|
||||
script = script.get_base_script()
|
||||
while script != null:
|
||||
scripts.append(script)
|
||||
script = script.get_base_script()
|
||||
# 显示脚本的数据
|
||||
show_script_list_data(scripts)
|
||||
|
||||
# 滚动到下面
|
||||
# await get_tree().create_timer(0.1).timeout
|
||||
# scroll_container.scroll_vertical = 2000
|
||||
|
||||
|
||||
## 展示脚本列表数据
|
||||
##[br]
|
||||
##[br][code]scripts[/code]
|
||||
func show_script_list_data(scripts: Array):
|
||||
item_list.clear()
|
||||
# 已添加过的(防止重复获取)
|
||||
var added : Dictionary = {}
|
||||
# 开始遍历
|
||||
scripts.reverse()
|
||||
for script in scripts:
|
||||
var path = script.resource_path.get_file()
|
||||
item_list.add_label(path)
|
||||
|
||||
# 获取数据
|
||||
var data = {}
|
||||
for method_data in script.get_script_method_list():
|
||||
var method_name : String = method_data['name']
|
||||
if not added.has(method_name):
|
||||
data[method_name] = method_data
|
||||
added[method_name] = null
|
||||
|
||||
# 排序
|
||||
var list = data.keys()
|
||||
list.sort()
|
||||
for key in list:
|
||||
var method_data : Dictionary = data[key]
|
||||
var method : String = method_data['name']
|
||||
item_list.add_item(method)
|
||||
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://btcf0syabq2et"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/script_comment_menu/sub_item/override/scene/script_method_list.gd" id="1_ao6ev"]
|
||||
[ext_resource type="PackedScene" uid="uid://dmiw7e671j5ox" path="res://addons/script_comment_menu/sub_item/override/scene/item_list.tscn" id="1_rynrx"]
|
||||
|
||||
[node name="ScriptMethodList" type="MarginContainer"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_ao6ev")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="."]
|
||||
offset_right = 1024.0
|
||||
offset_bottom = 600.0
|
||||
|
||||
[node name="ItemList" parent="ScrollContainer" instance=ExtResource("1_rynrx")]
|
|
@ -1,7 +0,0 @@
|
|||
class_name ScriptCommentMenuConstant
|
||||
|
||||
|
||||
const AddMenu = preload("res://addons/script_comment_menu/util/add_menu.gd")
|
||||
const MenuItemBuilder = preload("res://addons/script_comment_menu/util/menu_item_builder.gd")
|
||||
const PopupMenuUtil = preload("res://addons/script_comment_menu/util/popup_menu_util.gd")
|
||||
const ScriptEditorUtil = preload("res://addons/script_comment_menu/util/script_editor_util.gd")
|
|
@ -1,106 +0,0 @@
|
|||
extends EditorScript
|
||||
|
||||
|
||||
const EditorUtil_PopupMenu = preload("popup_menu_util.gd")
|
||||
|
||||
|
||||
var json = JSON.new()
|
||||
var util_popup_menu = EditorUtil_PopupMenu.new()
|
||||
|
||||
|
||||
func _run():
|
||||
pass
|
||||
|
||||
var menu = MenuButton.new()
|
||||
menu.text = "测试菜单"
|
||||
add_editor_menu(menu)
|
||||
await get_tree().create_timer(2).timeout
|
||||
menu.queue_free()
|
||||
|
||||
|
||||
|
||||
var _top_container: HBoxContainer
|
||||
func _get_top_container() -> HBoxContainer:
|
||||
if _top_container == null:
|
||||
for child in get_editor_interface().get_base_control().get_children():
|
||||
if child is VBoxContainer:
|
||||
_top_container = child.get_child(0)
|
||||
break
|
||||
return _top_container
|
||||
|
||||
var _editor_menu_container : HBoxContainer
|
||||
func get_editor_menu_container() -> HBoxContainer:
|
||||
if _editor_menu_container == null:
|
||||
_editor_menu_container = _get_top_container().get_child(0)
|
||||
return _editor_menu_container
|
||||
|
||||
|
||||
func add_editor_menu(menu_button: MenuButton):
|
||||
get_editor_menu_container().add_child(menu_button)
|
||||
|
||||
|
||||
func get_tree():
|
||||
return get_editor_interface().get_tree()
|
||||
|
||||
|
||||
## 添加脚本菜单按钮
|
||||
func add_script_editor_menu(menu_button: MenuButton, items: Array = []):
|
||||
var popup = menu_button.get_popup()
|
||||
for item in items:
|
||||
if item.begins_with("-"):
|
||||
popup.add_separator()
|
||||
else:
|
||||
while item.begins_with("-"):
|
||||
item = item.trim_prefix("-")
|
||||
popup.add_item(item)
|
||||
|
||||
var menu_container : Control
|
||||
while true:
|
||||
var tmp = get_editor_interface() \
|
||||
.get_script_editor() \
|
||||
.get_current_editor()
|
||||
if tmp == null:
|
||||
await Engine.get_main_loop().create_timer(1).timeout
|
||||
continue
|
||||
for i in 4:
|
||||
tmp = tmp.get_parent_control()
|
||||
if tmp == null:
|
||||
break
|
||||
if tmp == null:
|
||||
await Engine.get_main_loop().create_timer(1).timeout
|
||||
continue
|
||||
menu_container = tmp.get_child(0) as Control
|
||||
break
|
||||
|
||||
var node_index : int = 0
|
||||
for i in range(menu_container.get_child_count() - 1, -1, -1):
|
||||
if menu_container.get_child(i) is MenuButton:
|
||||
node_index = i + 1
|
||||
break
|
||||
menu_container.add_child(menu_button)
|
||||
menu_container.move_child(menu_button, node_index)
|
||||
|
||||
|
||||
func connect_menu(menu, item_name: String, callable, method: String = ""):
|
||||
var popup : PopupMenu
|
||||
if menu is MenuButton:
|
||||
popup = menu.get_popup()
|
||||
elif menu is PopupMenu:
|
||||
popup = menu
|
||||
if method:
|
||||
util_popup_menu.connect_popup_item(menu.get_popup(), item_name, callable, method)
|
||||
else:
|
||||
util_popup_menu.connect_popup_item(menu.get_popup(), item_name, callable.get_object(), callable.get_method())
|
||||
|
||||
|
||||
static func add_menu_item_shortcut(
|
||||
menu: MenuButton
|
||||
, item_name: String
|
||||
, keycode: int
|
||||
, ctrl : bool
|
||||
, alt : bool
|
||||
, shift : bool
|
||||
):
|
||||
EditorUtil_PopupMenu.add_popup_shortcut(
|
||||
menu.get_popup(), item_name, keycode, ctrl, alt, shift
|
||||
)
|
|
@ -1,139 +0,0 @@
|
|||
#============================================================
|
||||
# Menu Item Builder
|
||||
#============================================================
|
||||
# - datetime: 2022-07-17 14:14:42
|
||||
#============================================================
|
||||
# 菜单项建造器
|
||||
|
||||
var _menu : PopupMenu
|
||||
var _name : String
|
||||
var _method : Callable
|
||||
var _short : Dictionary = {
|
||||
key = KEY_NONE,
|
||||
ctrl = false,
|
||||
alt = false,
|
||||
shift = false,
|
||||
}
|
||||
var _as_separator : bool = false
|
||||
|
||||
var _id : int = -1
|
||||
|
||||
|
||||
#============================================================
|
||||
# Set/Get
|
||||
#============================================================
|
||||
## 设置添加的菜单
|
||||
##[br]
|
||||
##[br][code]menu[/code] 菜单
|
||||
func set_menu(menu: PopupMenu):
|
||||
self._menu = menu
|
||||
return self
|
||||
|
||||
## 设置菜单按钮对象
|
||||
func set_menu_by_menu_button(menu_button: MenuButton):
|
||||
set_menu( menu_button.get_popup() )
|
||||
return self
|
||||
|
||||
|
||||
## 设置菜单名
|
||||
##[br]
|
||||
##[br][code]name[/code]
|
||||
func set_item_name(name: String):
|
||||
self._name = name
|
||||
return self
|
||||
|
||||
## 设置连接的方法
|
||||
##[br]
|
||||
##[br][code]method[/code] 连接的方法
|
||||
func set_connect(method: Callable):
|
||||
self._method = method
|
||||
return self
|
||||
|
||||
## 设置快捷键
|
||||
##[br]
|
||||
##[br][code]key[/code] 按键 Key 值
|
||||
##[br][code]ctrl[/code] Ctrl
|
||||
##[br][code]alt[/code] Alt
|
||||
##[br][code]shift[/code] Shift
|
||||
func set_shortcut(
|
||||
key: int
|
||||
, ctrl: bool = false
|
||||
, alt: bool = false
|
||||
, shift: bool = false
|
||||
):
|
||||
self._short.key = key
|
||||
self._short.ctrl = ctrl
|
||||
self._short.alt = alt
|
||||
self._short.shift = shift
|
||||
return self
|
||||
|
||||
func set_key(key : int):
|
||||
self._short.key = key
|
||||
return self
|
||||
|
||||
func set_ctrl(value : bool = true):
|
||||
self._short.ctrl = value
|
||||
return self
|
||||
|
||||
func set_shift(value : bool = true):
|
||||
self._short.shift = value
|
||||
return self
|
||||
|
||||
func set_alt(value : bool = true):
|
||||
self._short.alt = value
|
||||
return self
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
## 实例化一个 Builder
|
||||
##[br]
|
||||
##[br][code]return[/code] 返回实例化对象
|
||||
static func instance():
|
||||
var builder = load("res://addons/script_comment_menu/util/menu_item_builder.gd").new()
|
||||
return builder
|
||||
|
||||
|
||||
## 添加分隔符
|
||||
func add_separator():
|
||||
_as_separator = true
|
||||
return self
|
||||
|
||||
|
||||
## 构建添加
|
||||
##[br]
|
||||
##[br][code]return[/code] 返回菜单的 id 值
|
||||
func build() -> int:
|
||||
_id = _menu.item_count
|
||||
|
||||
# 引用这个 builder,不这样则会因为引用消失而造成下面的 _id_pressed 失效
|
||||
_menu.set_meta("MenuItemMenu_%d" % _id, self)
|
||||
|
||||
if _menu.id_pressed.connect( _id_pressed ) != OK:
|
||||
printerr(" > id_pressed 信号连接方法失败")
|
||||
|
||||
if not _as_separator:
|
||||
_menu.add_item(_name)
|
||||
else:
|
||||
_menu.add_separator(_name)
|
||||
|
||||
if _short.key != KEY_NONE:
|
||||
var input = InputEventKey.new()
|
||||
input.keycode = _short.key
|
||||
input.ctrl_pressed = _short.ctrl
|
||||
input.alt_pressed = _short.alt
|
||||
input.shift_pressed = _short.shift
|
||||
var shortcut = Shortcut.new()
|
||||
shortcut.events.append(input)
|
||||
_menu.set_item_shortcut(_id, shortcut)
|
||||
|
||||
return _id
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
func _id_pressed(id):
|
||||
if id == _id:
|
||||
_method.call()
|
|
@ -1,62 +0,0 @@
|
|||
extends RefCounted
|
||||
|
||||
|
||||
static func find_popup_menu_id(popup: PopupMenu, item_name: String) -> int:
|
||||
for idx in popup.get_item_count():
|
||||
# 找到这个菜单
|
||||
if popup.get_item_text(idx) == item_name:
|
||||
return idx
|
||||
return -1
|
||||
|
||||
|
||||
static func add_popup_shortcut(
|
||||
popup: PopupMenu
|
||||
, item_name: String
|
||||
, keycode: int
|
||||
, ctrl : bool
|
||||
, alt : bool
|
||||
, shift : bool
|
||||
):
|
||||
var idx = find_popup_menu_id(popup, item_name)
|
||||
if idx > -1:
|
||||
var shortcut = Shortcut.new()
|
||||
var input = InputEventKey.new()
|
||||
input.keycode = keycode
|
||||
input.ctrl_pressed = ctrl
|
||||
input.alt_pressed = alt
|
||||
input.shift_pressed = shift
|
||||
shortcut.events.append(input)
|
||||
popup.set_item_shortcut(idx, shortcut)
|
||||
else:
|
||||
printerr("没有这个名称 ", item_name, " 的菜单项")
|
||||
|
||||
|
||||
|
||||
var _popup_data := {}
|
||||
func connect_popup_item(popup: PopupMenu, item_name: String, target: Object, method: String) -> int:
|
||||
var idx = find_popup_menu_id(popup, item_name)
|
||||
if idx > -1:
|
||||
if not popup.id_pressed.is_connected(self._popup_id_pressed):
|
||||
popup.id_pressed.connect(self._popup_id_pressed.bind(popup))
|
||||
if not _popup_data.has(popup):
|
||||
_popup_data[popup] = {}
|
||||
if not _popup_data[popup].has(idx):
|
||||
_popup_data[popup][idx] = []
|
||||
# 记录这个菜单的 id 的点击数据
|
||||
_popup_data[popup][idx].append({
|
||||
"target": target,
|
||||
"method": method,
|
||||
})
|
||||
else:
|
||||
printerr("这个菜单 popup 没有 ", item_name, " 的菜单项")
|
||||
|
||||
return idx
|
||||
|
||||
|
||||
func _popup_id_pressed(idx: int, popup: PopupMenu):
|
||||
if _popup_data.has(popup):
|
||||
var connected_data_list : Array = _popup_data[popup][idx]
|
||||
for data in connected_data_list:
|
||||
var target : Object = data['target']
|
||||
var method : String = data['method']
|
||||
target.call(method)
|
|
@ -1,65 +0,0 @@
|
|||
extends EditorScript
|
||||
|
||||
|
||||
## 当前代码编辑器
|
||||
func get_current_code_editor() -> TextEdit:
|
||||
return (get_editor_interface()
|
||||
.get_script_editor()
|
||||
.get_current_editor()
|
||||
.get_base_editor()) as TextEdit
|
||||
|
||||
|
||||
## 当前脚本的代码
|
||||
func get_current_script_code() -> String:
|
||||
return get_current_code_editor().text
|
||||
|
||||
|
||||
var _script_popup_id : int = -1
|
||||
var _script_popup := {}
|
||||
## 添加脚本弹窗
|
||||
## @return 返回添加的弹窗菜单的[code]id[/code]
|
||||
func add_script_popup(popup: PopupMenu) -> int:
|
||||
_script_popup_id += 1
|
||||
_script_popup[_script_popup_id] = popup
|
||||
get_editor_interface().get_script_editor().add_child(popup)
|
||||
return _script_popup_id
|
||||
|
||||
|
||||
## 弹出菜单
|
||||
## @id 菜单 [code]id[/code] 为 [method add_script_popup]add_script_popup[/method] 后返回的值
|
||||
func popup_menu(id: int):
|
||||
if _script_popup.has(id):
|
||||
var editor = get_current_code_editor() as TextEdit
|
||||
var popup : PopupMenu = _script_popup[id]
|
||||
popup.position = (get_current_code_editor().global_position
|
||||
+ get_current_code_editor().get_caret_draw_pos()
|
||||
+ Vector2(0, 50)
|
||||
)
|
||||
popup.popup()
|
||||
|
||||
func get_current_script() -> Script:
|
||||
return get_editor_interface() \
|
||||
.get_script_editor() \
|
||||
.get_current_script()
|
||||
|
||||
|
||||
func insert_code_current_pos(code: String, insert_first: bool = false):
|
||||
var textedit = get_current_code_editor()
|
||||
if insert_first:
|
||||
textedit.set_caret_column(0)
|
||||
textedit.insert_text_at_caret(code)
|
||||
|
||||
|
||||
func _run():
|
||||
pass
|
||||
|
||||
var popup = PopupMenu.new()
|
||||
popup.add_item("test_01")
|
||||
popup.add_item("test_02")
|
||||
popup.add_item("test_03")
|
||||
var id = add_script_popup(popup)
|
||||
popup_menu(id)
|
||||
|
||||
await get_editor_interface().get_tree().create_timer(1).timeout
|
||||
popup.queue_free()
|
||||
|
Binary file not shown.
|
@ -1,7 +0,0 @@
|
|||
[plugin]
|
||||
|
||||
name="table-data-editor"
|
||||
description=""
|
||||
author="张学徒"
|
||||
version="1.3"
|
||||
script="plugin.gd"
|
|
@ -1,64 +0,0 @@
|
|||
#============================================================
|
||||
# Plugin
|
||||
#============================================================
|
||||
# - datetime: 2022-11-27 22:27:12
|
||||
#============================================================
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
|
||||
const MAIN = preload("src/table_data_editor/table_data_editor.tscn")
|
||||
|
||||
var main := MAIN.instantiate() as TableDataEditor
|
||||
# 第一次显示出来
|
||||
var first_show := false
|
||||
|
||||
|
||||
func _ready():
|
||||
if Time.get_ticks_msec() < 5000:
|
||||
await Engine.get_main_loop().create_timer(5).timeout
|
||||
|
||||
main.visible = false
|
||||
get_editor_interface().get_editor_main_screen().add_child(main)
|
||||
main.call_deferred("set_anchors_preset", Control.PRESET_FULL_RECT)
|
||||
main.set_deferred("size", main.get_parent().size)
|
||||
main.get_child(0).set_deferred("size", main.size)
|
||||
|
||||
# 创建新文件时进行扫描
|
||||
main.created_file.connect(func(path):
|
||||
await Engine.get_main_loop().create_timer(0.1).timeout
|
||||
get_editor_interface() \
|
||||
.get_resource_filesystem() \
|
||||
.scan.call_deferred()
|
||||
)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
main.queue_free()
|
||||
|
||||
func _has_main_screen():
|
||||
return true
|
||||
|
||||
func _make_visible(visible):
|
||||
main.visible = visible
|
||||
|
||||
func _get_plugin_name():
|
||||
return "TableDataEditor"
|
||||
|
||||
func _get_plugin_icon():
|
||||
var icon = get_editor_interface() \
|
||||
.get_base_control() \
|
||||
.get_theme_icon("GridContainer", "EditorIcons") as Texture2D
|
||||
|
||||
icon = icon.duplicate(true)
|
||||
var image = icon.get_image() as Image
|
||||
var image_size = image.get_size()
|
||||
var color : Color
|
||||
for x in image_size.x:
|
||||
for y in image_size.y:
|
||||
color = image.get_pixel(x, y)
|
||||
if color.a != 0:
|
||||
color = get_editor_interface().get_editor_settings().get_setting("text_editor/theme/highlighting/text_color")
|
||||
image.set_pixel(x, y, color)
|
||||
var texture = ImageTexture.create_from_image(image)
|
||||
return texture
|
|
@ -1,86 +0,0 @@
|
|||
#============================================================
|
||||
# Project Data
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2023-05-17 17:37:56
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
## 项目数据
|
||||
##
|
||||
##整个表格项目中的之前的缓存数据,也可视作整个项目数据
|
||||
##[br]启动这个节点后
|
||||
class_name TableDataEditor_CacheData
|
||||
|
||||
|
||||
const CACHE_DATA_PATH = "res://.godot/table_data_editor/~json_edit_grid_cache_data.gdata"
|
||||
|
||||
|
||||
## 最后一次操作的文件的路径
|
||||
var last_operation_path : String = ""
|
||||
# 最近打开过的文件
|
||||
var recently_opend_paths = {}:
|
||||
set(v):
|
||||
if v is Array:
|
||||
recently_opend_paths = {}
|
||||
for item in v:
|
||||
recently_opend_paths[item] = null
|
||||
elif v is Dictionary:
|
||||
recently_opend_paths = v
|
||||
else:
|
||||
assert(false, "错误的数据类型")
|
||||
# 弹窗路径
|
||||
var dialog_path : String = ""
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
func get_recently_opend_paths() -> Array:
|
||||
if recently_opend_paths is Array:
|
||||
return recently_opend_paths
|
||||
return recently_opend_paths.keys()
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
static func instance() -> TableDataEditor_CacheData:
|
||||
var script = TableDataEditor_CacheData as GDScript
|
||||
const KEY = "instance"
|
||||
if script.has_meta(KEY):
|
||||
return script.get_meta(KEY)
|
||||
|
||||
else:
|
||||
var object = TableDataEditor_CacheData.new()
|
||||
var data
|
||||
if FileAccess.file_exists(CACHE_DATA_PATH):
|
||||
var bytes = FileAccess.get_file_as_bytes(CACHE_DATA_PATH)
|
||||
data = bytes_to_var(bytes)
|
||||
if data is Dictionary:
|
||||
TableDataUtil.Classes.set_property_by_dict(object, data)
|
||||
|
||||
script.set_meta(KEY, object)
|
||||
return object
|
||||
|
||||
|
||||
## 保存数据
|
||||
static func save_data() -> void:
|
||||
var data = TableDataUtil.Classes.get_dict_by_property(instance())
|
||||
TableDataUtil.Files.save_data(CACHE_DATA_PATH, data)
|
||||
|
||||
|
||||
## 存在打开的文件
|
||||
func exists_opened_path() -> bool:
|
||||
return last_operation_path != "" and FileAccess.file_exists(last_operation_path)
|
||||
|
||||
|
||||
func update_last_operation_path(path: String):
|
||||
path = path.strip_edges()
|
||||
last_operation_path = path
|
||||
if path != "":
|
||||
if not recently_opend_paths is Dictionary:
|
||||
recently_opend_paths = {}
|
||||
if FileAccess.file_exists(path):
|
||||
recently_opend_paths[path] = null
|
||||
|
|
@ -1,284 +0,0 @@
|
|||
#============================================================
|
||||
# Export Preview
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2022-11-27 17:45:09
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
# 预览导出
|
||||
@tool
|
||||
class_name ExportPreviewWindow
|
||||
extends Window
|
||||
|
||||
|
||||
## 最大示例数量
|
||||
const EXAMPLE_COUNT : int = 5
|
||||
|
||||
|
||||
## 导出 json 数据
|
||||
signal exported(path: String, type, data)
|
||||
|
||||
|
||||
@export_node_path("TableDataEditor") var _table_data_editor : NodePath
|
||||
@onready var table_data_editor : TableDataEditor = get_node(_table_data_editor)
|
||||
|
||||
var __init_node = InjectUtil.auto_inject(self, "_")
|
||||
var _text_box : TextEdit
|
||||
var _head_as_key_panel : Control
|
||||
var _head_line_box : SpinBox
|
||||
var _save_dialog : FileDialog
|
||||
var _compact : CheckBox
|
||||
var _select_items : Control
|
||||
var _selected_item_param : Control
|
||||
var _resource_class_name : LineEdit
|
||||
|
||||
|
||||
## 指定的 head 列数对应的值内容。_head_map[列数] = 值内容
|
||||
var _head_map : Dictionary = {}
|
||||
## 类型选项按钮组
|
||||
var _button_group : ButtonGroup
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _ready() -> void:
|
||||
_button_group = _select_items.get_child(0).button_group as ButtonGroup
|
||||
_button_group.pressed.connect(func(button):
|
||||
for child in _selected_item_param.get_children():
|
||||
if child is Control:
|
||||
child.visible = false
|
||||
|
||||
var item_node : Control = _selected_item_param.get_node_or_null(str(button.name))
|
||||
if item_node:
|
||||
item_node.visible = true
|
||||
|
||||
update_text_box_content()
|
||||
)
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
## 获取头部字段行。列值对应字段值
|
||||
func get_head_map() -> Dictionary:
|
||||
var head_row_number : int = _head_line_box.value
|
||||
var data_set = table_data_editor.get_table_edit().get_data_set()
|
||||
var head_row_data : Dictionary = data_set.grid_data.get(head_row_number, {})
|
||||
return head_row_data
|
||||
|
||||
|
||||
## 将有值的行的数据进行保存
|
||||
func get_data_by_head_row() -> Array:
|
||||
var result : Array = []
|
||||
var head_row_number : int = _head_line_box.value
|
||||
var data_set = table_data_editor.get_table_edit().get_data_set()
|
||||
var head_row_data : Dictionary = data_set.grid_data.get(head_row_number, {})
|
||||
head_row_number += 1
|
||||
for row in range(head_row_number, data_set.grid_data.size() + 1):
|
||||
var data = {}
|
||||
var row_data = data_set.grid_data[row]
|
||||
for column in head_row_data:
|
||||
data[head_row_data[column]] = row_data.get(column, "")
|
||||
result.append(data)
|
||||
return result
|
||||
|
||||
|
||||
## 获取 CSV 格式数据
|
||||
func get_csv_data() -> Array[String]:
|
||||
var data_set = table_data_editor.get_table_edit().data_set as TableDataEditor_TableDataSet
|
||||
var max_column : int = data_set.get_max_column()
|
||||
if max_column == 0:
|
||||
return []
|
||||
|
||||
var csv_list : Array[String] = []
|
||||
for row in data_set.get_row_list():
|
||||
var line : Array = []
|
||||
for column in range(1, max_column + 1):
|
||||
line.append(
|
||||
JSON.stringify(data_set.get_value(Vector2i(column, row)))
|
||||
)
|
||||
csv_list.append(",".join(line))
|
||||
|
||||
return csv_list
|
||||
|
||||
|
||||
## 获取转为资源的数据
|
||||
func get_resources_by_path(path: String) -> Array[Resource]:
|
||||
var head_row_data : Dictionary = get_head_map()
|
||||
var head_row_number : int = _head_line_box.value
|
||||
var data_set = table_data_editor.get_table_edit().get_data_set()
|
||||
var row_list = data_set.get_row_list()
|
||||
for idx in row_list.size():
|
||||
var row = row_list[idx]
|
||||
if row > head_row_number:
|
||||
row_list = row_list.slice(idx, row_list.size())
|
||||
break
|
||||
|
||||
# 类名
|
||||
var r_class_name : String = _resource_class_name.text.strip_edges()
|
||||
|
||||
# 属性列表
|
||||
var propertys : Array = []
|
||||
for column in head_row_data:
|
||||
|
||||
# 寻找这个字段有数据的行,判断数据类型
|
||||
var value = ""
|
||||
for row in row_list:
|
||||
value = data_set.grid_data[row].get(column)
|
||||
if value != "":
|
||||
break
|
||||
|
||||
# 判断数据类型
|
||||
var type = "String"
|
||||
if value != "":
|
||||
var json = JSON.parse_string(value)
|
||||
match typeof(json):
|
||||
TYPE_STRING, TYPE_NIL: type = "String"
|
||||
TYPE_INT: type = "int"
|
||||
TYPE_FLOAT: type = "float"
|
||||
TYPE_BOOL: type = "bool"
|
||||
TYPE_ARRAY: type = "Array"
|
||||
TYPE_DICTIONARY: type = "Dictionary"
|
||||
TYPE_COLOR: type = "Color"
|
||||
TYPE_VECTOR2: type = "Vector2"
|
||||
TYPE_VECTOR2I: type = "Vector2i"
|
||||
TYPE_VECTOR3: type = "Vector3"
|
||||
TYPE_VECTOR3I: type = "Vector3i"
|
||||
TYPE_VECTOR4: type = "Vector4"
|
||||
TYPE_VECTOR4I: type = "Vector4i"
|
||||
_: "String"
|
||||
|
||||
# 生成 @export s属性
|
||||
var property = head_row_data[column]
|
||||
printt(column, property, value)
|
||||
propertys.append("@export var %s : %s " % [property, type])
|
||||
|
||||
# 生成脚本
|
||||
var script = GDScript.new()
|
||||
script.source_code = """# {ScriptName}
|
||||
{ClassName}extends Resource
|
||||
|
||||
{Propertys}
|
||||
""".format({
|
||||
"ScriptName": path.get_file(),
|
||||
"ClassName": ("class_name %s\n" % [r_class_name]) if r_class_name else "",
|
||||
"Propertys": "\n".join(propertys),
|
||||
})
|
||||
ResourceSaver.save(script, path)
|
||||
|
||||
# 生成资源
|
||||
var resources : Array[Resource] = []
|
||||
var new_script = load(path) as GDScript
|
||||
for row in row_list:
|
||||
# 生成数据
|
||||
var data = {}
|
||||
var row_data = data_set.grid_data[row]
|
||||
for column in head_row_data:
|
||||
data[head_row_data[column]] = row_data.get(column, "")
|
||||
|
||||
# 生成资源。避免 new 时跟已有的类冲突造成的报错
|
||||
var resource = Resource.new()
|
||||
resource.set_script(new_script)
|
||||
for property in data:
|
||||
resource[property] = data[property]
|
||||
resources.append(resource)
|
||||
|
||||
return resources
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
func _data_format(data) -> String:
|
||||
return JSON.stringify(data, "" if _compact.button_pressed else "\t")
|
||||
|
||||
|
||||
func _update_by_head_row():
|
||||
var data_list = get_data_by_head_row()
|
||||
var examples = []
|
||||
for i in range(min(data_list.size(), EXAMPLE_COUNT)):
|
||||
examples.append(data_list[i])
|
||||
_text_box.text = JSON.stringify(examples, "\t")
|
||||
|
||||
|
||||
func _update_by_csv():
|
||||
var data_list = get_csv_data()
|
||||
var examples = []
|
||||
for i in range(min(data_list.size(), EXAMPLE_COUNT)):
|
||||
examples.append(data_list[i])
|
||||
_text_box.text = "\n".join(examples)
|
||||
|
||||
|
||||
# 更新文本框的内容
|
||||
func update_text_box_content():
|
||||
_text_box.text = ""
|
||||
match _button_group.get_pressed_button().name:
|
||||
"json", "resource":
|
||||
_update_by_head_row()
|
||||
"csv":
|
||||
_update_by_csv()
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
func _on_head_line_box_value_changed(value: float) -> void:
|
||||
update_text_box_content()
|
||||
|
||||
|
||||
func _on_export_pressed() -> void:
|
||||
var extension = str(_button_group.get_pressed_button().name)
|
||||
if extension == "resource":
|
||||
var r_class_name = _resource_class_name.text.strip_edges()
|
||||
if r_class_name == "":
|
||||
r_class_name = "res_0"
|
||||
_save_dialog.current_file = r_class_name.to_snake_case() + ".gd"
|
||||
|
||||
else:
|
||||
_save_dialog.current_file = "new_file." + extension
|
||||
|
||||
_save_dialog.popup_centered_ratio(0.5)
|
||||
|
||||
|
||||
func _on_save_dialog_file_selected(path: String) -> void:
|
||||
print(path)
|
||||
var data
|
||||
var type = str(_button_group.get_pressed_button().name)
|
||||
match type:
|
||||
"csv":
|
||||
data = "\n".join(get_csv_data())
|
||||
TableDataUtil.Files.save_as_string( path, data )
|
||||
|
||||
# 导出的文件保持默认文件,不作为翻译文件
|
||||
var keep_import_path = path + ".import"
|
||||
TableDataUtil.Files.save_as_string(keep_import_path, '[remap]\n\nimporter="keep"\n\n')
|
||||
|
||||
"json":
|
||||
data = get_data_by_head_row()
|
||||
TableDataUtil.Files.save_as_string( path, _data_format(data) )
|
||||
|
||||
"resource":
|
||||
data = get_resources_by_path(path)
|
||||
var idx : int = 0
|
||||
var dir = path.get_base_dir()
|
||||
var filename = path.get_file().get_basename()
|
||||
for resource in data:
|
||||
var file_path = dir.path_join("%s_%002d.tres" % [filename, idx])
|
||||
while FileAccess.file_exists(file_path):
|
||||
idx += 1
|
||||
file_path = dir.path_join("%s_%002d.tres" % [filename, idx])
|
||||
ResourceSaver.save(resource, file_path)
|
||||
idx += 1
|
||||
|
||||
_save_dialog.current_path = path
|
||||
|
||||
self.hide()
|
||||
print("[ ExportPreview ] 保存数据:", path)
|
||||
self.exported.emit(path, type, data )
|
||||
|
||||
|
||||
func _on_cancel_pressed():
|
||||
self.hide()
|
|
@ -1,181 +0,0 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://cqpmbxqgny2kq"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/export_preview/export_preview_window.gd" id="1_h8l6h"]
|
||||
|
||||
[sub_resource type="ButtonGroup" id="ButtonGroup_pxfjs"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_2ocp7"]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_s3ym3"]
|
||||
pressed = true
|
||||
keycode = 4194305
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_5m7cm"]
|
||||
events = [SubResource("InputEventKey_s3ym3")]
|
||||
|
||||
[node name="export_preview_window" type="Window"]
|
||||
title = "Export"
|
||||
size = Vector2i(800, 400)
|
||||
wrap_controls = true
|
||||
exclusive = true
|
||||
script = ExtResource("1_h8l6h")
|
||||
|
||||
[node name="export_preview" type="MarginContainer" parent="."]
|
||||
editor_description = "导出预览"
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="export_preview"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="export_preview"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 4
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 4
|
||||
theme_override_constants/margin_bottom = 8
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="export_preview/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HSplitContainer" type="HSplitContainer" parent="export_preview/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
split_offset = 200
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="select_items" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="csv" type="CheckBox" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/select_items"]
|
||||
layout_mode = 2
|
||||
button_pressed = true
|
||||
button_group = SubResource("ButtonGroup_pxfjs")
|
||||
text = "CSV"
|
||||
|
||||
[node name="json" type="CheckBox" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/select_items"]
|
||||
layout_mode = 2
|
||||
button_group = SubResource("ButtonGroup_pxfjs")
|
||||
text = "JSON"
|
||||
|
||||
[node name="resource" type="CheckBox" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/select_items"]
|
||||
layout_mode = 2
|
||||
button_group = SubResource("ButtonGroup_pxfjs")
|
||||
text = "Resource"
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 16
|
||||
|
||||
[node name="selected_item_param" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[node name="csv" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="Label" type="Label" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/csv"]
|
||||
layout_mode = 2
|
||||
text = "delim"
|
||||
|
||||
[node name="LineEdit" type="LineEdit" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/csv"]
|
||||
layout_mode = 2
|
||||
text = ","
|
||||
placeholder_text = "表格间的分隔符,默认为 ,"
|
||||
|
||||
[node name="json" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/json"]
|
||||
layout_mode = 2
|
||||
tooltip_text = "作为 key 的行"
|
||||
text = "Key Row: "
|
||||
|
||||
[node name="head_line_box" type="SpinBox" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/json"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "作为 key 的行"
|
||||
min_value = 1.0
|
||||
max_value = 10000.0
|
||||
value = 1.0
|
||||
|
||||
[node name="compact" type="CheckBox" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/json"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
tooltip_text = "紧凑的格式导出,这样JSON会占用空间会最小。数据量不是非常大可以不用勾选"
|
||||
text = "compact"
|
||||
|
||||
[node name="resource" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param"]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/resource"]
|
||||
layout_mode = 2
|
||||
text = "ClassName"
|
||||
|
||||
[node name="resource_class_name" type="LineEdit" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/resource"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
placeholder_text = "Class Name"
|
||||
|
||||
[node name="Label" type="Label" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer"]
|
||||
modulate = Color(1, 1, 1, 0.462745)
|
||||
layout_mode = 2
|
||||
text = "Sample data in the first few lines..."
|
||||
|
||||
[node name="text_box" type="TextEdit" parent="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
editable = false
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="export_preview/MarginContainer/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Control" type="Control" parent="export_preview/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="export" type="Button" parent="export_preview/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(100, 32)
|
||||
layout_mode = 2
|
||||
text = "Export"
|
||||
|
||||
[node name="VSeparator" type="VSeparator" parent="export_preview/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 32
|
||||
theme_override_styles/separator = SubResource("StyleBoxEmpty_2ocp7")
|
||||
|
||||
[node name="cancel" type="Button" parent="export_preview/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
custom_minimum_size = Vector2(80, 0)
|
||||
layout_mode = 2
|
||||
shortcut = SubResource("Shortcut_5m7cm")
|
||||
text = "Cancel"
|
||||
|
||||
[node name="Control3" type="Control" parent="export_preview/MarginContainer/VBoxContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="save_dialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
size = Vector2i(375, 161)
|
||||
access = 2
|
||||
filters = PackedStringArray("*.json; JSON", "*.csv; CSV", "*.tres; TRES", "*.res; RES", "*.gd; GDScript")
|
||||
|
||||
[connection signal="value_changed" from="export_preview/MarginContainer/VBoxContainer/HSplitContainer/VBoxContainer/HBoxContainer/selected_item_param/json/head_line_box" to="." method="_on_head_line_box_value_changed"]
|
||||
[connection signal="pressed" from="export_preview/MarginContainer/VBoxContainer/HBoxContainer/export" to="." method="_on_export_pressed"]
|
||||
[connection signal="pressed" from="export_preview/MarginContainer/VBoxContainer/HBoxContainer/cancel" to="." method="_on_cancel_pressed"]
|
||||
[connection signal="file_selected" from="save_dialog" to="." method="_on_save_dialog_file_selected"]
|
|
@ -1,106 +0,0 @@
|
|||
#============================================================
|
||||
# File Data
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2023-03-23 23:30:46
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
## 当前文件数据
|
||||
##
|
||||
##新建/打开/存储 处理其中的数据。
|
||||
##[br]文件相关的数据都在这里保存,加载存储获取都在这里,这样保证数据不会乱
|
||||
class_name TableDataEditor_FileData
|
||||
|
||||
|
||||
## 原始数据
|
||||
var origin_data : Dictionary
|
||||
# 数据格式版本
|
||||
var version:
|
||||
get: return 1_3_0
|
||||
# 数据集
|
||||
var data_set = TableDataEditor_TableDataSet.new():
|
||||
set(v):
|
||||
data_set = TableDataEditor_TableDataSet.new(v) \
|
||||
if v is Dictionary \
|
||||
else v
|
||||
## 列宽数据
|
||||
var column_width : Dictionary = {}
|
||||
## 行高数据
|
||||
var row_height : Dictionary = {}
|
||||
## 编辑框大小
|
||||
var edit_dialog_size : Vector2 = Vector2(100, 50)
|
||||
## 文件路径
|
||||
var path : String = ""
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
func is_empty() -> bool:
|
||||
return origin_data.is_empty()
|
||||
|
||||
func is_new_file() -> bool:
|
||||
return path.is_empty()
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _init(origin_data: Dictionary):
|
||||
self.origin_data = origin_data
|
||||
|
||||
# 加载旧版本数据
|
||||
var version = origin_data.get("version")
|
||||
if not ((version is float or version is int) and version >= 1.3):
|
||||
var grid_data : Dictionary = origin_data.get("grid_data", {})
|
||||
for coords in grid_data:
|
||||
self.data_set.set_value(coords, grid_data[coords])
|
||||
|
||||
# 根据字典数据设置当前对象属性
|
||||
TableDataUtil.Classes.set_property_by_dict(self, origin_data)
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
## 加载文件。
|
||||
static func load_file(path: String) -> TableDataEditor_FileData:
|
||||
if FileAccess.file_exists(path):
|
||||
var data = TableDataUtil.Files.load_file(path)
|
||||
if not data is Dictionary:
|
||||
var reader = FileAccess.open(path, FileAccess.READ)
|
||||
if reader:
|
||||
data = reader.get_var()
|
||||
|
||||
if not data is Dictionary:
|
||||
data = {}
|
||||
|
||||
var file_data = TableDataEditor_FileData.new(data)
|
||||
file_data.path = path
|
||||
return file_data
|
||||
else:
|
||||
return TableDataEditor_FileData.new({})
|
||||
|
||||
|
||||
## 保存当前文件的数据
|
||||
##[br]
|
||||
##[br][code]saved_path[/code] 保存到的路径,默认不传参数保存为当前路径,
|
||||
##如果当前没有设置路径则会报错,所以第一次要传入路径进行保存
|
||||
func save_data(saved_path: String = "") -> bool:
|
||||
if saved_path == "":
|
||||
saved_path = self.path
|
||||
self.path = saved_path
|
||||
|
||||
assert(self.path.strip_edges() != "", "当前没有设置文件路径")
|
||||
|
||||
# 获取数据
|
||||
var data : Dictionary = {}
|
||||
var propertys = TableDataUtil.Classes.get_propertys(self.get_script())
|
||||
for property in propertys:
|
||||
if property in self:
|
||||
data[property] = self[property]
|
||||
|
||||
data["data_set"] = data_set.get_config_data()
|
||||
|
||||
return TableDataUtil.Files.save_data(self.path, data)
|
||||
|
|
@ -1,331 +0,0 @@
|
|||
#============================================================
|
||||
# Menu List
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2022-11-27 01:01:10
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
## 菜单列表
|
||||
@tool
|
||||
class_name MenuList
|
||||
extends MenuBar
|
||||
|
||||
|
||||
## 菜单被点击
|
||||
##[br]
|
||||
##[br][code]idx[/code] 菜单的索引值
|
||||
##[br][code]menu_path[/code] 菜单路径
|
||||
signal menu_pressed(idx: int, menu_path: StringName)
|
||||
## 复选框状态发生切换
|
||||
signal menu_check_toggled(idx: int, menu_path: StringName)
|
||||
|
||||
|
||||
# 自动增长的菜单 idx。用以下面添加菜单项时记录添加的菜单的 idx
|
||||
var _auto_increment_menu_idx := -1
|
||||
# 菜单路径对应 PopupMenu
|
||||
var _menu_path_to_popup_menu_map := {}
|
||||
# 菜单的 idx 对应的菜单路径
|
||||
var _idx_to_menu_path_map := {}
|
||||
# 菜单路径对应的菜单 idx
|
||||
var _menu_path_to_idx_map := {}
|
||||
# 子节点路径
|
||||
var _child_menu_path_idx_list := {}
|
||||
|
||||
|
||||
#=====================================================
|
||||
# Set/Get
|
||||
#=====================================================
|
||||
## 获取弹窗菜单
|
||||
##[br]
|
||||
##[br][code]menu_path[/code] 这个菜单路径的父弹窗菜单节点
|
||||
func get_menu(menu_path: StringName) -> PopupMenu:
|
||||
return _menu_path_to_popup_menu_map.get(menu_path) as PopupMenu
|
||||
|
||||
|
||||
## 添加快捷键
|
||||
##[br]
|
||||
##[br][code]menu_path[/code] 菜单路径
|
||||
##[br][code]data[/code] 快捷键数据,示例数据:ctrl + shift + C 快捷键
|
||||
##[codeblock]
|
||||
##{
|
||||
## "ctrl": true,
|
||||
## "shift": true,
|
||||
## "keycode": KEY_C,
|
||||
##}
|
||||
##[/codeblock]
|
||||
func set_menu_shortcut(menu_path: StringName, data: Dictionary):
|
||||
var shortcut = Shortcut.new()
|
||||
var input = InputEventKey.new()
|
||||
shortcut.events.append(input)
|
||||
input.keycode = data.get("keycode", 0)
|
||||
input.ctrl_pressed = data.get("ctrl", false)
|
||||
input.alt_pressed = data.get("alt", false)
|
||||
input.shift_pressed = data.get("shift", false)
|
||||
|
||||
var popup_menu := _menu_path_to_popup_menu_map.get(menu_path) as PopupMenu
|
||||
var menu_name = menu_path.get_file()
|
||||
if popup_menu:
|
||||
for i in popup_menu.item_count:
|
||||
if popup_menu.get_item_text(i) == menu_name:
|
||||
popup_menu.set_item_shortcut(i, shortcut)
|
||||
break
|
||||
|
||||
|
||||
|
||||
# 获取菜单的 ID
|
||||
func _get_menu_id(menu_path: String) -> int:
|
||||
return _menu_path_to_idx_map.get(menu_path, -1)
|
||||
|
||||
func _execute_menu_by_path(menu_path: StringName, method_name: String, params: Array = []):
|
||||
var menu = get_menu(menu_path)
|
||||
var idx = get_menu_idx(menu_path)
|
||||
if menu and idx > 0:
|
||||
return menu.call(method_name, params)
|
||||
return null
|
||||
|
||||
## 设置菜单的可用性
|
||||
func set_menu_disabled_by_path(menu_path: StringName, value: bool):
|
||||
var menu = get_menu(menu_path)
|
||||
var idx = get_menu_idx(menu_path)
|
||||
if menu and idx > -1:
|
||||
menu.set_item_disabled(idx, value)
|
||||
|
||||
func set_menu_as_checkable(menu_path: StringName, value: bool):
|
||||
var menu = get_menu(menu_path)
|
||||
var idx = get_menu_idx(menu_path)
|
||||
if menu and idx > 0:
|
||||
menu.set_item_as_checkable(idx, value)
|
||||
|
||||
func set_menu_check_by_path(menu_path: StringName, value: bool):
|
||||
var menu = get_menu(menu_path)
|
||||
var idx = get_menu_idx(menu_path)
|
||||
if menu and idx > 0 and menu.is_item_checked(idx) != value:
|
||||
menu.set_item_checked(idx, value)
|
||||
self.menu_check_toggled.emit(idx, menu_path)
|
||||
|
||||
func get_menu_check_by_path(menu_path: StringName) -> bool:
|
||||
var menu = get_menu(menu_path)
|
||||
var idx = get_menu_idx(menu_path)
|
||||
if menu and idx > 0:
|
||||
return menu.is_item_checked(idx)
|
||||
return false
|
||||
|
||||
func toggle_menu_check_by_path(menu_path: StringName) -> bool:
|
||||
return _execute_menu_by_path(menu_path, "is_item_checked", [])
|
||||
|
||||
## 获取这个菜单的索引,如果不存在这个菜单,则返回 [code]-1[/code]
|
||||
func get_menu_idx(menu_path: StringName) -> int:
|
||||
var id = _menu_path_to_idx_map.get(menu_path, -1)
|
||||
var menu = get_menu(menu_path)
|
||||
if menu == null:
|
||||
var parent_path = get_parent_menu_path(menu_path)
|
||||
menu = get_menu(parent_path)
|
||||
return menu.get_item_index(id)
|
||||
|
||||
|
||||
## 获取这个索引的菜单路径
|
||||
func get_menu_path(menu_path: StringName) -> StringName:
|
||||
var idx = get_menu_idx(menu_path)
|
||||
return _idx_to_menu_path_map.get(idx, "")
|
||||
|
||||
|
||||
## 是否有这个菜单路径
|
||||
func has_menu_path(menu_path: StringName) -> bool:
|
||||
return _menu_path_to_popup_menu_map.has(menu_path)
|
||||
|
||||
|
||||
## 获取父菜单路径
|
||||
func get_parent_menu_path(menu_path: StringName) -> StringName:
|
||||
var idx : int = _menu_path_to_idx_map.get(menu_path, -1)
|
||||
if idx == -1:
|
||||
return ""
|
||||
for parent_path in _child_menu_path_idx_list:
|
||||
var list = _child_menu_path_idx_list[parent_path] as Array
|
||||
if list.has(idx):
|
||||
return parent_path
|
||||
return ""
|
||||
|
||||
|
||||
#=====================================================
|
||||
# 自定义方法
|
||||
#=====================================================
|
||||
## 初始化菜单。示例:
|
||||
## [codeblock]
|
||||
## init_menu({
|
||||
## "File": ["Open", "Save", "Save As", {
|
||||
## "Export As...": [ "Export PNG", "Export JPG" ]
|
||||
## } ],
|
||||
## "item": {
|
||||
## "letter": ["a", "b", "c"],
|
||||
## "number": [ "1", "2"],
|
||||
## },
|
||||
## })
|
||||
## [/codeblock]
|
||||
func init_menu(data: Dictionary):
|
||||
add_menu(data, "/")
|
||||
|
||||
|
||||
## 初始化快捷键,需要添加对应菜单。示例:
|
||||
##[codeblock]
|
||||
##{
|
||||
## "/File/Open": {"keycode": KEY_O, "ctrl": true},
|
||||
## "/File/Save": {"keycode": KEY_S, "ctrl": true},
|
||||
##}
|
||||
##[/codeblock]
|
||||
func init_shortcut(data_list: Dictionary):
|
||||
var data : Dictionary
|
||||
for menu_path in data_list:
|
||||
data = data_list[menu_path]
|
||||
set_menu_shortcut(menu_path, data)
|
||||
|
||||
|
||||
## 添加菜单项
|
||||
##[br]
|
||||
##[br][code]menu_data[/code] 这个菜单项包含的数据
|
||||
##[br][code]parent_menu_path[/code] 父级菜单路径
|
||||
func add_menu(menu_data, parent_menu_path: StringName):
|
||||
var parent_popup_menu : PopupMenu = get_menu(parent_menu_path)
|
||||
|
||||
_auto_increment_menu_idx += 1
|
||||
|
||||
# 不是根路径时
|
||||
if parent_menu_path != "/":
|
||||
# Dictionary
|
||||
if menu_data is Dictionary:
|
||||
for menu_name in menu_data:
|
||||
add_menu( menu_data[menu_name], parent_menu_path.path_join(menu_name))
|
||||
|
||||
# Array
|
||||
elif menu_data is Array:
|
||||
for data in menu_data:
|
||||
add_menu(data, parent_menu_path)
|
||||
|
||||
# String
|
||||
elif menu_data is String or menu_data is StringName:
|
||||
# 添加菜单
|
||||
if not _menu_path_to_popup_menu_map.has(parent_menu_path):
|
||||
create_menu(parent_menu_path, null)
|
||||
parent_popup_menu = get_menu(parent_menu_path)
|
||||
# 不是 Array 和 Dictionary 类型时,只能是 String 类型了
|
||||
var menu_name := StringName(menu_data)
|
||||
if not menu_name.begins_with("-"):
|
||||
var menu_path := "%s/%s" % [parent_menu_path, menu_name]
|
||||
# 添加菜单项
|
||||
parent_popup_menu.add_item(menu_name, _auto_increment_menu_idx)
|
||||
_idx_to_menu_path_map[_auto_increment_menu_idx] = menu_path
|
||||
_menu_path_to_idx_map[menu_path] = _auto_increment_menu_idx
|
||||
_menu_path_to_popup_menu_map[menu_path] = parent_popup_menu
|
||||
if not _child_menu_path_idx_list.has(parent_menu_path):
|
||||
_child_menu_path_idx_list[parent_menu_path] = []
|
||||
_child_menu_path_idx_list[parent_menu_path].append(_auto_increment_menu_idx)
|
||||
|
||||
else:
|
||||
parent_popup_menu.add_separator()
|
||||
|
||||
else:
|
||||
assert(false, "错误的数据类型:" + str(typeof(menu_data)) )
|
||||
|
||||
else:
|
||||
|
||||
# 根菜单按钮
|
||||
for menu_name in menu_data:
|
||||
# 添加菜单按钮
|
||||
|
||||
var menu := PopupMenu.new()
|
||||
menu.name = menu_name
|
||||
add_child(menu)
|
||||
|
||||
# 设置属性
|
||||
var menu_path = parent_menu_path.path_join(menu_name)
|
||||
_set_popup_menu(menu_path, menu)
|
||||
|
||||
# 添加这个按钮菜单的子菜单
|
||||
add_menu(menu_data[menu_name], menu_path)
|
||||
|
||||
|
||||
## 移除菜单
|
||||
func remove_menu(menu_path: StringName) -> bool:
|
||||
if has_menu_path(menu_path):
|
||||
# 移除子菜单及其数据
|
||||
var child_menu_idx_list = _child_menu_path_idx_list.get(menu_path, [])
|
||||
for child_menu_idx in child_menu_idx_list:
|
||||
var child_menu_path = get_menu_idx(child_menu_idx)
|
||||
if has_menu_path(child_menu_path):
|
||||
remove_menu(child_menu_path)
|
||||
|
||||
# 移除自身数据
|
||||
var idx = get_menu_idx(menu_path)
|
||||
|
||||
# 移除菜单节点
|
||||
var menu = get_menu(menu_path) as PopupMenu
|
||||
if menu != null:
|
||||
menu.queue_free()
|
||||
else:
|
||||
var parent_menu_path = get_parent_menu_path(menu_path)
|
||||
var parent_menu = get_menu(parent_menu_path)
|
||||
parent_menu.remove_item(idx)
|
||||
|
||||
_menu_path_to_popup_menu_map.erase(menu_path)
|
||||
_menu_path_to_idx_map.erase(menu_path)
|
||||
_idx_to_menu_path_map.erase(idx)
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
## 清空菜单
|
||||
func clear_menu(menu_path: StringName) -> bool:
|
||||
if has_menu_path(menu_path):
|
||||
var menu = get_menu(menu_path)
|
||||
if menu != null:
|
||||
menu.clear()
|
||||
return true
|
||||
return false
|
||||
|
||||
|
||||
## 创建菜单
|
||||
##[br]
|
||||
##[br][code]menu_path[/code] 菜单路径
|
||||
##[br][code]parent_menu[/code] 父级菜单
|
||||
func create_menu(menu_path: StringName, parent_menu: PopupMenu):
|
||||
# 切分菜单名
|
||||
var parent_menu_names := menu_path.split("/")
|
||||
# 因为切分后 0 索引都是空字符串,所以移除
|
||||
parent_menu_names.remove_at(0)
|
||||
|
||||
# 逐个添加菜单
|
||||
parent_menu = get_menu("/" + "/".join(parent_menu_names.slice(0, 1)))
|
||||
for i in parent_menu_names.size():
|
||||
var sub_menu_path = "/" + "/".join(parent_menu_names.slice(0, i + 1))
|
||||
# 没有这个菜单则添加
|
||||
if not _menu_path_to_popup_menu_map.has(sub_menu_path):
|
||||
var menu_name = parent_menu_names[i]
|
||||
var menu_popup = _create_popup_menu(sub_menu_path)
|
||||
_menu_path_to_popup_menu_map[sub_menu_path] = menu_popup
|
||||
parent_menu.add_child(menu_popup)
|
||||
parent_menu.add_submenu_item( menu_name, menu_name )
|
||||
# 开始记录这个菜单,用以这个菜单的下一级别的菜单
|
||||
parent_menu = get_menu(sub_menu_path)
|
||||
|
||||
|
||||
|
||||
#=====================================================
|
||||
# 连接信号
|
||||
#=====================================================
|
||||
# 创建这个路径的菜单
|
||||
func _create_popup_menu(path: StringName) -> PopupMenu:
|
||||
var menu_popup = PopupMenu.new()
|
||||
menu_popup.name = path.get_file()
|
||||
_set_popup_menu(path, menu_popup)
|
||||
return menu_popup
|
||||
|
||||
|
||||
# 设置菜单属性
|
||||
func _set_popup_menu(menu_path: StringName, menu_popup: PopupMenu):
|
||||
self._menu_path_to_popup_menu_map[menu_path] = menu_popup
|
||||
# 点击菜单时
|
||||
menu_popup.id_pressed.connect(func(id):
|
||||
self.menu_pressed.emit(id, _idx_to_menu_path_map[id])
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -1,456 +0,0 @@
|
|||
#============================================================
|
||||
# Json Editor
|
||||
#============================================================
|
||||
# - datetime: 2022-11-27 01:31:14
|
||||
#============================================================
|
||||
## JSON 数据编辑器
|
||||
##
|
||||
##这里将各独立的组件组合起来,功能整合
|
||||
@tool
|
||||
class_name TableDataEditor
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
## 没有保存文件时的颜色
|
||||
const NOT_SAVED_COLOR = Color(1, 0.65, 0.275, 1)
|
||||
## 保存文件后的颜色
|
||||
const SAVED_COLOR = Color(1, 1, 1, 0.625)
|
||||
## 最大显示的最近打开的文件数量
|
||||
const RECENTLY_OPEND_MAX_COUNT = 10
|
||||
|
||||
|
||||
## 文件弹窗文件过滤
|
||||
const FILTERS = ["*.gdata; GData"]
|
||||
## 菜单项数据
|
||||
const MENU_ITEM : Dictionary = {
|
||||
"File": [
|
||||
"New", "Open", {"Recently Opened": ["/"]}, "-",
|
||||
"Save", "Save As...", "-",
|
||||
"Export...",
|
||||
"Import...",
|
||||
],
|
||||
"Edit": [
|
||||
"Undo", "Redo", "-",
|
||||
"Double click edit"
|
||||
],
|
||||
"Help": ["Help"],
|
||||
}
|
||||
## 菜单快捷键
|
||||
const MENU_SHORTCUT : Dictionary = {
|
||||
"/File/New": { "keycode": KEY_N, "ctrl": true },
|
||||
"/File/Open": { "keycode": KEY_O, "ctrl": true },
|
||||
"/File/Save": { "keycode": KEY_S, "ctrl": true },
|
||||
"/File/Save As...": { "keycode": KEY_S, "ctrl": true, "shift": true },
|
||||
"/File/Export...": { "keycode": KEY_E, "ctrl": true },
|
||||
"/File/Import...": { "keycode": KEY_I, "ctrl": true },
|
||||
"/Edit/Undo": {"keycode": KEY_Z, "ctrl": true},
|
||||
"/Edit/Redo": {"keycode": KEY_Z, "ctrl": true, "shift": true},
|
||||
}
|
||||
const MENU_CHECKABLE : Array = [
|
||||
"/Edit/Double click edit",
|
||||
]
|
||||
|
||||
|
||||
## 创建新的文件
|
||||
signal created_file(path: String)
|
||||
|
||||
|
||||
# 保存到的文件路径
|
||||
var _saved_path : String = "" :
|
||||
set(v):
|
||||
_saved_path = v
|
||||
_file_path_label.text = _saved_path
|
||||
# 是否已保存
|
||||
var _saved: bool = true:
|
||||
set(v):
|
||||
_saved = v
|
||||
if _saved_status_label == null:
|
||||
await ready
|
||||
if _saved:
|
||||
_saved_status_label.text = "(saved)"
|
||||
_saved_status_label.self_modulate = SAVED_COLOR
|
||||
else:
|
||||
_saved_status_label.text = "(unsaved)"
|
||||
_saved_status_label.self_modulate = NOT_SAVED_COLOR
|
||||
|
||||
# 行映射。记录哪些行有数据
|
||||
var _has_value_row_map := {}
|
||||
# 列映射。记录哪些列有数据
|
||||
var _has_value_column_map := {}
|
||||
# 撤销重做
|
||||
var _undo_redo : UndoRedo = UndoRedo.new()
|
||||
|
||||
# 上次打开的文件路径
|
||||
var _dialog_path : String = ""
|
||||
# 是否已加载完成
|
||||
var _is_reloaded := false :
|
||||
set(v):
|
||||
if _is_reloaded == false:
|
||||
_is_reloaded = v
|
||||
|
||||
|
||||
var __init_node = InjectUtil.auto_inject(self, "_")
|
||||
|
||||
|
||||
var _table_edit : TableEdit # 编辑表格节点
|
||||
var _menu_list : MenuList # 菜单列表
|
||||
var _scroll_pos : LineEdit # 滚动条位置输入框
|
||||
var _pages : ItemList # 切换页面(暂未开始实现功能)
|
||||
var _export_preview_window : ExportPreviewWindow
|
||||
var _confirm_dialog : ConfirmationDialog
|
||||
var _tooltip_dialog : AcceptDialog
|
||||
var _save_as_dialog : FileDialog
|
||||
var _open_file_dialog : FileDialog
|
||||
var _import_dialog : FileDialog
|
||||
var _saved_status_label : Label
|
||||
var _file_path_label : Label
|
||||
var _prompt_message : Label
|
||||
var _prompt_message_player : AnimationPlayer
|
||||
|
||||
|
||||
var file_data := TableDataEditor_FileData.new({})
|
||||
var cache_data := TableDataEditor_CacheData.instance()
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
## 获取编辑表格对象
|
||||
func get_table_edit() -> TableEdit:
|
||||
return _table_edit
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _ready() -> void:
|
||||
file_data = TableDataEditor_FileData.new({})
|
||||
cache_data = TableDataEditor_CacheData.instance()
|
||||
|
||||
(func():
|
||||
_saved_path = ""
|
||||
|
||||
_init_dialog()
|
||||
_init_menu()
|
||||
|
||||
_load_last_cache_data()
|
||||
|
||||
_is_reloaded = true
|
||||
|
||||
).call_deferred()
|
||||
|
||||
|
||||
func _exit_tree():
|
||||
if not Engine.is_editor_hint() or TableDataUtil.Editor.is_enabled():
|
||||
cache_data.save_data()
|
||||
|
||||
|
||||
#============================================================
|
||||
# 私有方法
|
||||
#============================================================
|
||||
# 新建文件
|
||||
func _new_file() -> void:
|
||||
load_file_path("")
|
||||
|
||||
|
||||
# 初始化菜单列表
|
||||
func _init_menu():
|
||||
# TODO: 最近打开的文件替换增加数据
|
||||
_menu_list.init_menu(MENU_ITEM)
|
||||
# 设置快捷键
|
||||
_menu_list.init_shortcut(MENU_SHORTCUT)
|
||||
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Undo", true)
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Redo", true)
|
||||
|
||||
for menu_path in MENU_CHECKABLE:
|
||||
_menu_list.set_menu_as_checkable(menu_path, true)
|
||||
_menu_list.set_menu_check_by_path("/Edit/Double click edit", true)
|
||||
|
||||
|
||||
# 初始化弹窗
|
||||
func _init_dialog():
|
||||
|
||||
# 数据导出预览
|
||||
_export_preview_window.close_requested.connect( func(): _export_preview_window.visible = false )
|
||||
|
||||
# 添加文件类型var FILTERS = ["*.gdata; GData"]
|
||||
_open_file_dialog.filters = FILTERS
|
||||
_save_as_dialog.filters = FILTERS
|
||||
_import_dialog.filters = ["*.csv; CSV"]
|
||||
|
||||
# 打开窗口的路径位置
|
||||
var callable = func(dialog: FileDialog):
|
||||
if dialog.current_dir != _dialog_path:
|
||||
_dialog_path = dialog.current_dir
|
||||
_open_file_dialog.visibility_changed.connect(callable.bind(_open_file_dialog))
|
||||
_save_as_dialog.visibility_changed.connect(callable.bind(_save_as_dialog))
|
||||
|
||||
|
||||
# 加载上次缓存的数据
|
||||
func _load_last_cache_data():
|
||||
for dialog in [_open_file_dialog, _save_as_dialog, _import_dialog]:
|
||||
dialog.current_dir = cache_data.dialog_path
|
||||
dialog.visibility_changed.connect(func():
|
||||
if not dialog.visible:
|
||||
cache_data.dialog_path = dialog.current_dir
|
||||
)
|
||||
|
||||
if cache_data.exists_opened_path():
|
||||
load_file_path(cache_data.last_operation_path)
|
||||
|
||||
# 添加打开过的路径
|
||||
const RECENTLY_OPEND_MENU = "/File/Recently Opened"
|
||||
var list : Array = cache_data.get_recently_opend_paths()
|
||||
list.reverse()
|
||||
for idx in range(min(list.size(), RECENTLY_OPEND_MAX_COUNT)):
|
||||
var path = list[idx]
|
||||
if FileAccess.file_exists(path):
|
||||
_menu_list.add_menu(path, RECENTLY_OPEND_MENU)
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
## 显示提示信息
|
||||
func display_prompt_message(message: String, color: Color = Color.WHITE) -> void:
|
||||
_prompt_message.text = message
|
||||
_prompt_message.modulate = color
|
||||
_prompt_message_player.stop()
|
||||
_prompt_message_player.play("flicker")
|
||||
|
||||
|
||||
## 加载路径的数据
|
||||
##[br]
|
||||
##[br][code]path[/code] 加载这个路径的数据,如果为空字符串,则是为临时数据,保存时会弹窗保存位置
|
||||
func load_file_path(path: String):
|
||||
# 这个文件的数据
|
||||
load_file_data(TableDataEditor_FileData.load_file(path))
|
||||
|
||||
_saved_path = path
|
||||
|
||||
cache_data.update_last_operation_path(_saved_path)
|
||||
cache_data.save_data()
|
||||
|
||||
|
||||
## 加载文件数据
|
||||
##[br]
|
||||
##[br][code]file_data[/code] 加载文件数据
|
||||
func load_file_data(file_data: TableDataEditor_FileData):
|
||||
self.file_data = file_data
|
||||
|
||||
# 加载到表格中
|
||||
(func():
|
||||
_table_edit.row_to_height_map = file_data.row_height
|
||||
_table_edit.column_to_width_map = file_data.column_width
|
||||
_table_edit.data_set = file_data.data_set
|
||||
|
||||
_table_edit.get_edit_dialog().box_size = file_data.edit_dialog_size
|
||||
_table_edit.update_cell_list()
|
||||
).call_deferred()
|
||||
|
||||
# 其他
|
||||
_saved = true
|
||||
_saved_path = ""
|
||||
|
||||
_undo_redo.clear_history()
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Undo", true)
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Redo", true)
|
||||
|
||||
_table_edit.get_edit_dialog().showed = false
|
||||
|
||||
|
||||
## 保存数据到这个路径中
|
||||
func save_data_to(path: String):
|
||||
if file_data.save_data(path):
|
||||
# 保存成功,则进行处理
|
||||
self._saved = true
|
||||
self._saved_path = path
|
||||
cache_data.update_last_operation_path(path)
|
||||
cache_data.save_data()
|
||||
|
||||
self.created_file.emit(path)
|
||||
|
||||
display_prompt_message("保存成功. %s" % [Time.get_datetime_string_from_system().replace("T", " ")])
|
||||
print("[ TableDataEditor ] 保存成功 ", Time.get_datetime_string_from_system())
|
||||
|
||||
else:
|
||||
display_prompt_message("保存失败")
|
||||
printerr("[ TableDataEditor ] 保存失败")
|
||||
|
||||
|
||||
## 保存为 JSON
|
||||
func save_as_json(path: String):
|
||||
var data = _table_edit.data_set.get_origin_data()
|
||||
TableDataUtil.Files.save_as_string(path, data)
|
||||
self.created_file.emit(path)
|
||||
|
||||
|
||||
## 显示保存 Dialog
|
||||
func show_save_dialog(default_file_name: String = ""):
|
||||
if default_file_name != "":
|
||||
_save_as_dialog.current_file = default_file_name
|
||||
_save_as_dialog.popup_centered_ratio(0.5)
|
||||
|
||||
|
||||
## 导入文件
|
||||
func import_file(path: String):
|
||||
const FILE_TYPE = ["csv"]
|
||||
if not path.get_extension() in FILE_TYPE:
|
||||
display_prompt_message("错误的文件类型:%s。暂不支持 %s 以外的文件类型" % [
|
||||
path.get_extension(), FILE_TYPE
|
||||
])
|
||||
assert(false, "错误的文件类型")
|
||||
|
||||
# 加载 csv数据 到 数据集 中
|
||||
var data_set = TableDataEditor_TableDataSet.new()
|
||||
var csv_lines = TableDataUtil.Files.read_csv_file(path)
|
||||
|
||||
var line : PackedStringArray
|
||||
for row in csv_lines.size():
|
||||
line = csv_lines[row]
|
||||
for column in line.size():
|
||||
data_set.set_value(Vector2i(column, row) + Vector2i.ONE, line[column])
|
||||
|
||||
# 加载数据
|
||||
var tmp_file_data = file_data.load_file("")
|
||||
tmp_file_data.data_set = data_set
|
||||
load_file_data(tmp_file_data)
|
||||
|
||||
cache_data.dialog_path = path
|
||||
cache_data.save_data()
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
func _on_table_edit_cell_value_changed(cell: InputCell, coords: Vector2i, previous: String, value: String):
|
||||
_saved = false
|
||||
|
||||
# print("[ TableDataEditor ] 单元格发生改变")
|
||||
|
||||
# 记录存在有数据的行列
|
||||
_undo_redo.create_action("修改单元格的值")
|
||||
_undo_redo.add_do_method( _table_edit.alter_value.bind(coords, value, false) )
|
||||
_undo_redo.add_do_method( _table_edit.update_cell_list )
|
||||
_undo_redo.add_undo_method( _table_edit.alter_value.bind(coords, previous, false) )
|
||||
_undo_redo.add_undo_method( _table_edit.update_cell_list )
|
||||
_undo_redo.commit_action()
|
||||
|
||||
# 撤销可用性
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Undo", false)
|
||||
|
||||
|
||||
func _on_table_edit_scroll_changed(coords: Vector2i):
|
||||
_scroll_pos.text = str(coords)
|
||||
|
||||
|
||||
func _on_scroll_pos_text_submitted(new_text):
|
||||
var re = RegEx.new()
|
||||
re.compile("(\\d+)\\s*,\\s*(\\d+)")
|
||||
var result = re.search(new_text)
|
||||
if result == null:
|
||||
return
|
||||
|
||||
var pos = str_to_var("Vector2i(%s, %s)" % [result.get_string(1), result.get_string(2)])
|
||||
if pos is Vector2i:
|
||||
_table_edit.scroll_to(pos)
|
||||
print("[ TableDataEditor ] 跳转到位置:", pos)
|
||||
|
||||
|
||||
func _on_menu_list_menu_pressed(idx, menu_path: StringName):
|
||||
# print_debug("[ TableDataEditor ] 点击菜单 ", menu_path)
|
||||
|
||||
match menu_path:
|
||||
"/File/New":
|
||||
if not _saved:
|
||||
_confirm_dialog.dialog_text = "当前还没有保存,是否要继续创建?"
|
||||
_confirm_dialog.popup_centered()
|
||||
else:
|
||||
_new_file()
|
||||
|
||||
"/File/Open":
|
||||
_open_file_dialog.popup_centered_ratio(0.5)
|
||||
|
||||
"/File/Save":
|
||||
if _saved_path == "":
|
||||
show_save_dialog()
|
||||
else:
|
||||
save_data_to(_saved_path)
|
||||
|
||||
"/File/Save As...":
|
||||
show_save_dialog("new_file.gdata")
|
||||
|
||||
"/File/Export...":
|
||||
_export_preview_window.popup_centered_ratio(0.5)
|
||||
_export_preview_window.update_text_box_content()
|
||||
|
||||
"/File/Import...":
|
||||
_import_dialog.popup_centered_ratio(0.5)
|
||||
|
||||
"/Edit/Undo":
|
||||
_undo_redo.undo()
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Undo", not _undo_redo.has_undo())
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Redo", false)
|
||||
|
||||
"/Edit/Redo":
|
||||
_undo_redo.redo()
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Redo", not _undo_redo.has_redo())
|
||||
_menu_list.set_menu_disabled_by_path("/Edit/Undo", false)
|
||||
|
||||
"/Edit/Double click edit":
|
||||
var status = _menu_list.get_menu_check_by_path("/Edit/Double click edit")
|
||||
_menu_list.set_menu_check_by_path("/Edit/Double click edit", not status)
|
||||
_table_edit.double_click_edit = not status
|
||||
|
||||
"/Help/Help":
|
||||
_tooltip_dialog.popup_centered()
|
||||
|
||||
_:
|
||||
# 最近打开的文件
|
||||
if menu_path.contains("/File/Recently Opened"):
|
||||
var file_path = menu_path.trim_prefix("/File/Recently Opened/")
|
||||
if FileAccess.file_exists(file_path):
|
||||
load_file_path(file_path)
|
||||
|
||||
else:
|
||||
printerr("文件不存在")
|
||||
_menu_list.remove_menu(menu_path)
|
||||
|
||||
|
||||
func _on_save_as_dialog_file_selected(path):
|
||||
_saved_path = path
|
||||
match _saved_path.get_extension():
|
||||
"gdata":
|
||||
save_data_to( _saved_path )
|
||||
"json":
|
||||
save_as_json( _saved_path )
|
||||
_:
|
||||
display_prompt_message("错误的文件类型:%s" % [ _saved_path.get_extension() ])
|
||||
printerr("[ TableDataEditor ] <Unknown Type> ", _saved_path.get_extension())
|
||||
|
||||
|
||||
func _on_export_preview_window_exported(path, type, data):
|
||||
_export_preview_window.visible = false
|
||||
display_prompt_message("已导出 %s 资源" % [type])
|
||||
self.created_file.emit(path)
|
||||
|
||||
|
||||
func _on_open_file_dialog_file_selected(path: String):
|
||||
cache_data.dialog_path = path.get_base_dir()
|
||||
load_file_path(path)
|
||||
|
||||
|
||||
func _on_file_path_label_gui_input(event):
|
||||
if event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT and event.double_click:
|
||||
if _saved_path != "" and DirAccess.dir_exists_absolute(_saved_path.get_base_dir()):
|
||||
var path = TableDataUtil.Files.get_absolute_path(_saved_path)
|
||||
OS.shell_open(path.get_base_dir())
|
||||
|
||||
|
||||
func _on_table_edit_popup_edit_box_size_changed(box_size):
|
||||
file_data.edit_dialog_size = box_size
|
||||
|
|
@ -1,233 +0,0 @@
|
|||
[gd_scene load_steps=8 format=3 uid="uid://68vjwfxquvlf"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_data_editor.gd" id="1_j3oce"]
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/menu_list.gd" id="2_o7gjv"]
|
||||
[ext_resource type="PackedScene" uid="uid://ctppgkl2dpksd" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_edit.tscn" id="3_87xax"]
|
||||
[ext_resource type="PackedScene" uid="uid://cqpmbxqgny2kq" path="res://addons/table_data_editor/src/table_data_editor/export_preview/export_preview_window.tscn" id="4_8p413"]
|
||||
|
||||
[sub_resource type="Animation" id="Animation_jmtv6"]
|
||||
length = 0.001
|
||||
|
||||
[sub_resource type="Animation" id="Animation_lfmqt"]
|
||||
resource_name = "flicker"
|
||||
length = 8.0
|
||||
tracks/0/type = "value"
|
||||
tracks/0/imported = false
|
||||
tracks/0/enabled = true
|
||||
tracks/0/path = NodePath(".:modulate:a")
|
||||
tracks/0/interp = 1
|
||||
tracks/0/loop_wrap = true
|
||||
tracks/0/keys = {
|
||||
"times": PackedFloat32Array(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 5, 8),
|
||||
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
|
||||
"update": 0,
|
||||
"values": [0.0, 0.55, 1.0, 0.55, 1.0, 0.55, 1.0, 0.55, 1.0, 1.0, 0.0]
|
||||
}
|
||||
|
||||
[sub_resource type="AnimationLibrary" id="AnimationLibrary_xid6c"]
|
||||
_data = {
|
||||
"RESET": SubResource("Animation_jmtv6"),
|
||||
"flicker": SubResource("Animation_lfmqt")
|
||||
}
|
||||
|
||||
[node name="table_data_editor" type="MarginContainer"]
|
||||
clip_contents = true
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 2
|
||||
theme_override_constants/margin_top = 2
|
||||
theme_override_constants/margin_right = 2
|
||||
theme_override_constants/margin_bottom = 2
|
||||
script = ExtResource("1_j3oce")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="menu_list" type="MenuBar" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 1
|
||||
flat = true
|
||||
script = ExtResource("2_o7gjv")
|
||||
|
||||
[node name="GridContainer" type="HSplitContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
split_offset = 120
|
||||
|
||||
[node name="pages" type="ItemList" parent="VBoxContainer/GridContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="table_edit" parent="VBoxContainer/GridContainer" instance=ExtResource("3_87xax")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="PanelContainer" type="PanelContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/PanelContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 4
|
||||
theme_override_constants/margin_top = 2
|
||||
theme_override_constants/margin_right = 4
|
||||
theme_override_constants/margin_bottom = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="scroll_pos" type="LineEdit" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
focus_mode = 1
|
||||
text = "(1, 1)"
|
||||
alignment = 1
|
||||
select_all_on_focus = true
|
||||
|
||||
[node name="MarginContainer3" type="MarginContainer" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 2
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 2
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="Panel" type="Panel" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/MarginContainer3"]
|
||||
custom_minimum_size = Vector2(1, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Control" type="HBoxContainer" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
focus_mode = 1
|
||||
|
||||
[node name="prompt_message" type="Label" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/Control"]
|
||||
unique_name_in_owner = true
|
||||
modulate = Color(1, 1, 1, 0)
|
||||
layout_mode = 2
|
||||
text = "(Prompt Message)"
|
||||
|
||||
[node name="prompt_message_player" type="AnimationPlayer" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/Control/prompt_message"]
|
||||
unique_name_in_owner = true
|
||||
libraries = {
|
||||
"": SubResource("AnimationLibrary_xid6c")
|
||||
}
|
||||
|
||||
[node name="MarginContainer2" type="MarginContainer" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 2
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 2
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="Panel" type="Panel" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/MarginContainer2"]
|
||||
custom_minimum_size = Vector2(1, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="file_path_label" type="Label" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
tooltip_text = "双击打开所在文件目录"
|
||||
focus_mode = 1
|
||||
mouse_filter = 0
|
||||
text = "res://addons/table_data_editor/column_width_test.gdata"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 2
|
||||
theme_override_constants/margin_top = 4
|
||||
theme_override_constants/margin_right = 2
|
||||
theme_override_constants/margin_bottom = 4
|
||||
|
||||
[node name="Panel" type="Panel" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/MarginContainer"]
|
||||
custom_minimum_size = Vector2(1, 0)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="saved_status_label" type="Label" parent="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
self_modulate = Color(1, 1, 1, 0.625)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
focus_mode = 1
|
||||
text = "(saved)"
|
||||
|
||||
[node name="export_preview_window" parent="." instance=ExtResource("4_8p413")]
|
||||
unique_name_in_owner = true
|
||||
position = Vector2i(0, -500)
|
||||
visible = false
|
||||
_table_data_editor = NodePath("..")
|
||||
|
||||
[node name="confirm_dialog" type="ConfirmationDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
[node name="tooltip_dialog" type="AcceptDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
gui_embed_subwindows = true
|
||||
title = "Help"
|
||||
size = Vector2i(500, 317)
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="tooltip_dialog"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="Label" type="Label" parent="tooltip_dialog/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
text = "双击单元格进行编辑
|
||||
|
||||
按住 Alt 键进行左右滚动,松开即可上下滚动
|
||||
|
||||
按住 Tab 键或 Enter 键进行切换到下一个编辑的单元格,如果同时按下 Shift 则是切换到上一个单元格
|
||||
|
||||
|
||||
"
|
||||
|
||||
[node name="save_as_dialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
filters = PackedStringArray("*.gdata; GData")
|
||||
|
||||
[node name="open_file_dialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Open a File"
|
||||
size = Vector2i(312, 157)
|
||||
ok_button_text = "打开"
|
||||
file_mode = 0
|
||||
filters = PackedStringArray("*.gdata; GData")
|
||||
|
||||
[node name="import_dialog" type="FileDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Open a File"
|
||||
size = Vector2i(295, 161)
|
||||
ok_button_text = "打开"
|
||||
file_mode = 0
|
||||
access = 2
|
||||
filters = PackedStringArray("*.csv; CSV")
|
||||
|
||||
[connection signal="menu_pressed" from="VBoxContainer/menu_list" to="." method="_on_menu_list_menu_pressed"]
|
||||
[connection signal="cell_value_changed" from="VBoxContainer/GridContainer/table_edit" to="." method="_on_table_edit_cell_value_changed"]
|
||||
[connection signal="popup_edit_box_size_changed" from="VBoxContainer/GridContainer/table_edit" to="." method="_on_table_edit_popup_edit_box_size_changed"]
|
||||
[connection signal="scroll_changed" from="VBoxContainer/GridContainer/table_edit" to="." method="_on_table_edit_scroll_changed"]
|
||||
[connection signal="text_submitted" from="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/scroll_pos" to="." method="_on_scroll_pos_text_submitted"]
|
||||
[connection signal="gui_input" from="VBoxContainer/PanelContainer/MarginContainer/HBoxContainer/file_path_label" to="." method="_on_file_path_label_gui_input"]
|
||||
[connection signal="exported" from="export_preview_window" to="." method="_on_export_preview_window_exported"]
|
||||
[connection signal="confirmed" from="confirm_dialog" to="." method="_new_file"]
|
||||
[connection signal="file_selected" from="save_as_dialog" to="." method="_on_save_as_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="open_file_dialog" to="." method="_on_open_file_dialog_file_selected"]
|
||||
[connection signal="file_selected" from="import_dialog" to="." method="import_file"]
|
|
@ -1,13 +0,0 @@
|
|||
#============================================================
|
||||
# Base Cell Element
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 22:25:48
|
||||
#============================================================
|
||||
## 基础的单元格元素
|
||||
@tool
|
||||
class_name BaseCellElement
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/base_cell_element.gd" id="1_wjxc7"]
|
||||
|
||||
[node name="base_cell_element" type="MarginContainer"]
|
||||
offset_right = 80.0
|
||||
offset_bottom = 32.0
|
||||
focus_mode = 2
|
||||
theme_override_constants/margin_right = 4
|
||||
theme_override_constants/margin_bottom = 4
|
||||
script = ExtResource("1_wjxc7")
|
|
@ -1,145 +0,0 @@
|
|||
#============================================================
|
||||
# Input Cell
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 18:12:34
|
||||
#============================================================
|
||||
##输入单元格
|
||||
@tool
|
||||
class_name InputCell
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
##单击单元格
|
||||
signal single_clicked
|
||||
##双击单元格
|
||||
signal double_clicked
|
||||
##水平方向拖拽
|
||||
##[br]
|
||||
##[br][code]distance[/code] 为当前鼠标位置与第一次按下时鼠标位置的距离
|
||||
##[br][code]pressed_size[/code] 为点击时的节点的大小
|
||||
signal h_dragged(distance: float, pressed_node_size: Vector2i)
|
||||
##垂直方向拖拽
|
||||
##[br]
|
||||
##[br][code]distance[/code] 为当前鼠标位置与第一次按下时鼠标位置的距离
|
||||
##[br][code]pressed_size[/code] 为点击时的节点的大小
|
||||
signal v_dragged(distance: float, pressed_node_size: Vector2i)
|
||||
|
||||
|
||||
var _data : String
|
||||
var _latest_v_dragged := false
|
||||
var _latest_h_dragged := false
|
||||
var _latest_dragged := false
|
||||
|
||||
# 点击时的鼠标位置
|
||||
var _pressed_mouse_pos: Vector2
|
||||
# 点击时节点的大小
|
||||
var _pressed_node_size: Vector2i
|
||||
# 是否可以拖拽,防止 _event 发送速度过快
|
||||
var _enabled_dragged := true
|
||||
|
||||
|
||||
@onready
|
||||
var _text_edit := %text_edit as TextEdit
|
||||
@onready
|
||||
var _process_timer := %process_timer as Timer
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
func set_value(v: String):
|
||||
_data = v
|
||||
show_value(v)
|
||||
|
||||
func get_value() -> String:
|
||||
return _data
|
||||
|
||||
func show_value(v):
|
||||
_text_edit.text = v if v else ""
|
||||
|
||||
## 值发生了改变
|
||||
func is_changed():
|
||||
return _text_edit.text != _data
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _ready():
|
||||
# _text_edit.gui_input.connect(self._gui_input)
|
||||
|
||||
_text_edit.focus_entered.connect( func(): self.focus_entered.emit() )
|
||||
_text_edit.focus_exited.connect( func():
|
||||
self.focus_exited.emit()
|
||||
if is_changed():
|
||||
set_value(_text_edit.text)
|
||||
)
|
||||
set_process(false)
|
||||
_process_timer.timeout.connect( set_process.bind(false) )
|
||||
self.mouse_entered.connect(func():
|
||||
set_process(true)
|
||||
_process_timer.stop()
|
||||
)
|
||||
self.mouse_exited.connect(func():
|
||||
_process_timer.start()
|
||||
)
|
||||
|
||||
|
||||
|
||||
func _process(delta):
|
||||
# 更新显示的鼠标图像
|
||||
var margin = custom_minimum_size - get_local_mouse_position()
|
||||
if margin.x < self["theme_override_constants/margin_right"] and margin.y < self["theme_override_constants/margin_bottom"]:
|
||||
self.mouse_default_cursor_shape = Control.CURSOR_MOVE
|
||||
elif margin.x <= self["theme_override_constants/margin_right"]:
|
||||
self.mouse_default_cursor_shape = Control.CURSOR_HSPLIT
|
||||
elif margin.y <= self["theme_override_constants/margin_bottom"]:
|
||||
self.mouse_default_cursor_shape = Control.CURSOR_VSPLIT
|
||||
else:
|
||||
if not(_latest_h_dragged or _latest_v_dragged):
|
||||
self.mouse_default_cursor_shape = Control.CURSOR_ARROW
|
||||
|
||||
_enabled_dragged = true
|
||||
|
||||
|
||||
func _gui_input(event):
|
||||
if event is InputEventMouseMotion:
|
||||
if _enabled_dragged:
|
||||
var diff = get_local_mouse_position() - _pressed_mouse_pos
|
||||
if _latest_h_dragged:
|
||||
self.custom_minimum_size.x = _pressed_node_size.x + diff.x
|
||||
h_dragged.emit(self.custom_minimum_size.x - _pressed_node_size.x, _pressed_node_size)
|
||||
_process_timer.stop()
|
||||
if _latest_v_dragged:
|
||||
self.custom_minimum_size.y = _pressed_node_size.y + diff.y
|
||||
v_dragged.emit(self.custom_minimum_size.y - _pressed_node_size.y, _pressed_node_size)
|
||||
_process_timer.stop()
|
||||
_enabled_dragged = false
|
||||
|
||||
elif event is InputEventMouseButton:
|
||||
if event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
|
||||
var margin = custom_minimum_size - get_local_mouse_position()
|
||||
if event.double_click:
|
||||
self.double_clicked.emit()
|
||||
else:
|
||||
|
||||
_pressed_node_size = self.size
|
||||
_pressed_mouse_pos = get_local_mouse_position()
|
||||
|
||||
if margin.x <= self["theme_override_constants/margin_right"]:
|
||||
_latest_h_dragged = true
|
||||
if margin.y <= self["theme_override_constants/margin_bottom"]:
|
||||
_latest_v_dragged = true
|
||||
|
||||
self.single_clicked.emit()
|
||||
|
||||
else:
|
||||
self.mouse_default_cursor_shape = Control.CURSOR_ARROW
|
||||
_latest_h_dragged = false
|
||||
_latest_v_dragged = false
|
||||
|
||||
var diff = self.custom_minimum_size - get_local_mouse_position()
|
||||
if diff.x < 0 or diff.y < 0:
|
||||
_process_timer.start()
|
||||
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
[gd_scene load_steps=16 format=3]
|
||||
|
||||
[ext_resource type="PackedScene" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/base_cell_element.tscn" id="1_ney3p"]
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/input_cell/input_cell.gd" id="2_y6m17"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_54uk8"]
|
||||
|
||||
[sub_resource type="Image" id="Image_pbgwo"]
|
||||
data = {
|
||||
"data": PackedByteArray(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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 3, 255, 255, 255, 41, 255, 255, 255, 67, 255, 255, 255, 67, 255, 255, 255, 40, 255, 255, 255, 3, 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, 41, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 40, 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, 67, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 67, 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, 67, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 67, 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, 40, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 74, 255, 255, 255, 40, 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, 3, 255, 255, 255, 40, 255, 255, 255, 67, 255, 255, 255, 67, 255, 255, 255, 40, 255, 255, 255, 3, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 12,
|
||||
"mipmaps": false,
|
||||
"width": 12
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_o2enw"]
|
||||
image = SubResource("Image_pbgwo")
|
||||
|
||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_hq7i2"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
texture = SubResource("ImageTexture_o2enw")
|
||||
margin_left = 6.0
|
||||
margin_top = 6.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
region_rect = Rect2(0, 0, 12, 12)
|
||||
|
||||
[sub_resource type="Image" id="Image_s7tle"]
|
||||
data = {
|
||||
"data": PackedByteArray(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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 6, 248, 248, 248, 102, 249, 249, 249, 168, 249, 249, 249, 168, 248, 248, 248, 101, 213, 213, 213, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 248, 248, 248, 102, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 248, 248, 248, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 249, 249, 249, 168, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 249, 249, 249, 168, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 248, 248, 248, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 248, 248, 248, 101, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 249, 249, 249, 186, 250, 250, 250, 99, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 213, 213, 213, 6, 248, 248, 248, 101, 249, 249, 249, 168, 248, 248, 248, 168, 250, 250, 250, 99, 213, 213, 213, 6, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 12,
|
||||
"mipmaps": false,
|
||||
"width": 12
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_pi2w1"]
|
||||
image = SubResource("Image_s7tle")
|
||||
|
||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_daefy"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
texture = SubResource("ImageTexture_pi2w1")
|
||||
margin_left = 5.0
|
||||
margin_top = 5.0
|
||||
margin_right = 5.0
|
||||
margin_bottom = 5.0
|
||||
region_rect = Rect2(0, 0, 12, 12)
|
||||
|
||||
[sub_resource type="Image" id="Image_4ojul"]
|
||||
data = {
|
||||
"data": PackedByteArray(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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 213, 213, 213, 6, 180, 180, 180, 102, 181, 181, 181, 168, 181, 181, 181, 168, 179, 179, 179, 101, 170, 170, 170, 6, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 180, 180, 180, 102, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 179, 179, 179, 101, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 181, 181, 181, 168, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 181, 181, 181, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 181, 181, 181, 168, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 179, 179, 179, 168, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 179, 179, 179, 101, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 180, 180, 180, 186, 181, 181, 181, 99, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 170, 170, 170, 6, 179, 179, 179, 101, 181, 181, 181, 168, 179, 179, 179, 168, 181, 181, 181, 99, 170, 170, 170, 6, 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, 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, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
|
||||
"format": "RGBA8",
|
||||
"height": 12,
|
||||
"mipmaps": false,
|
||||
"width": 12
|
||||
}
|
||||
|
||||
[sub_resource type="ImageTexture" id="ImageTexture_tvj35"]
|
||||
image = SubResource("Image_4ojul")
|
||||
|
||||
[sub_resource type="StyleBoxTexture" id="StyleBoxTexture_x7jas"]
|
||||
content_margin_left = 2.0
|
||||
content_margin_top = 2.0
|
||||
content_margin_right = 2.0
|
||||
content_margin_bottom = 2.0
|
||||
texture = SubResource("ImageTexture_tvj35")
|
||||
margin_left = 6.0
|
||||
margin_top = 6.0
|
||||
margin_right = 6.0
|
||||
margin_bottom = 6.0
|
||||
region_rect = Rect2(0, 0, 12, 12)
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_eycgy"]
|
||||
|
||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_64aem"]
|
||||
|
||||
[sub_resource type="Theme" id="Theme_cy8yd"]
|
||||
HScrollBar/icons/decrement = null
|
||||
HScrollBar/icons/decrement_highlight = null
|
||||
HScrollBar/icons/decrement_pressed = null
|
||||
HScrollBar/icons/increment = null
|
||||
HScrollBar/icons/increment_highlight = null
|
||||
HScrollBar/icons/increment_pressed = null
|
||||
HScrollBar/styles/grabber = null
|
||||
HScrollBar/styles/grabber_highlight = null
|
||||
HScrollBar/styles/grabber_pressed = null
|
||||
HScrollBar/styles/scroll = SubResource("StyleBoxEmpty_54uk8")
|
||||
HScrollBar/styles/scroll_focus = null
|
||||
VScrollBar/icons/decrement = null
|
||||
VScrollBar/icons/decrement_highlight = null
|
||||
VScrollBar/icons/decrement_pressed = null
|
||||
VScrollBar/icons/increment = null
|
||||
VScrollBar/icons/increment_highlight = null
|
||||
VScrollBar/icons/increment_pressed = null
|
||||
VScrollBar/styles/grabber = SubResource("StyleBoxTexture_hq7i2")
|
||||
VScrollBar/styles/grabber_highlight = SubResource("StyleBoxTexture_daefy")
|
||||
VScrollBar/styles/grabber_pressed = SubResource("StyleBoxTexture_x7jas")
|
||||
VScrollBar/styles/scroll = SubResource("StyleBoxEmpty_eycgy")
|
||||
VScrollBar/styles/scroll_focus = SubResource("StyleBoxEmpty_64aem")
|
||||
|
||||
[node name="input_cell" instance=ExtResource("1_ney3p")]
|
||||
custom_minimum_size = Vector2(80, 35)
|
||||
offset_bottom = 35.0
|
||||
mouse_filter = 0
|
||||
script = ExtResource("2_y6m17")
|
||||
|
||||
[node name="text_edit" type="TextEdit" parent="." index="0"]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 76.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 2
|
||||
theme = SubResource("Theme_cy8yd")
|
||||
|
||||
[node name="process_timer" type="Timer" parent="." index="1"]
|
||||
unique_name_in_owner = true
|
||||
wait_time = 2.0
|
||||
one_shot = true
|
|
@ -1,19 +0,0 @@
|
|||
#============================================================
|
||||
# Serial Number Cell
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 22:33:13
|
||||
#============================================================
|
||||
# 序列号表格
|
||||
@tool
|
||||
class_name SerialNumberCell
|
||||
extends BaseCellElement
|
||||
|
||||
|
||||
@onready
|
||||
var label := $label as Label
|
||||
|
||||
|
||||
func show_number(v: int):
|
||||
if label == null: await ready
|
||||
label.text = str(v)
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
[gd_scene load_steps=3 format=3]
|
||||
|
||||
[ext_resource type="PackedScene" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/base_cell_element.tscn" id="1_5yxaj"]
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/serial_number_cell/serial_number_cell.gd" id="2_bf00s"]
|
||||
|
||||
[node name="serial_number_cell" instance=ExtResource("1_5yxaj")]
|
||||
offset_right = 50.0
|
||||
offset_bottom = 30.0
|
||||
theme_override_constants/margin_right = 8
|
||||
script = ExtResource("2_bf00s")
|
||||
|
||||
[node name="label" type="Label" parent="." index="0"]
|
||||
offset_right = 42.0
|
||||
offset_bottom = 26.0
|
||||
size_flags_vertical = 1
|
||||
text = "0"
|
||||
vertical_alignment = 1
|
|
@ -1,136 +0,0 @@
|
|||
#============================================================
|
||||
# Edit Box Window
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2023-03-19 11:48:30
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
@tool
|
||||
class_name PopupEditBox
|
||||
extends Control
|
||||
|
||||
|
||||
signal popup_hide(text: String)
|
||||
signal box_size_changed(box_size: Vector2)
|
||||
signal input_switch_char(character: int)
|
||||
|
||||
|
||||
@export
|
||||
var text : String = "" :
|
||||
set(v):
|
||||
text = v
|
||||
if not is_inside_tree(): await ready
|
||||
if _edit_box.text != text:
|
||||
_edit_box.text = text
|
||||
@export
|
||||
var showed : bool = true:
|
||||
set(v):
|
||||
if v != self.visible:
|
||||
showed = v
|
||||
self.visible = v
|
||||
@export
|
||||
var box_size: Vector2 :
|
||||
set(v):
|
||||
box_size = v
|
||||
if not is_inside_tree(): await ready
|
||||
if _edit_box.size != box_size:
|
||||
_edit_box.size = box_size
|
||||
get:
|
||||
if _edit_box:
|
||||
return _edit_box.size
|
||||
return Vector2(0, 0)
|
||||
|
||||
|
||||
@onready var _edit_box := %edit_box as TextEdit
|
||||
@onready var _scale_rect := %scale_rect as Control
|
||||
|
||||
|
||||
var _resize_pressed : bool = false
|
||||
var _pressed_size : Vector2 = Vector2(0,0)
|
||||
var _pressed_pos : Vector2 = Vector2(0,0)
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
func get_edit_box() -> TextEdit:
|
||||
return _edit_box
|
||||
|
||||
func get_text() -> String:
|
||||
return _edit_box.text
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _ready():
|
||||
_edit_box.position = Vector2(0,0)
|
||||
|
||||
_scale_rect.gui_input.connect(func(event):
|
||||
if event is InputEventMouseMotion:
|
||||
if _resize_pressed:
|
||||
var diff_v = get_global_mouse_position() - _pressed_pos
|
||||
_edit_box.size = _pressed_size + diff_v
|
||||
|
||||
elif event is InputEventMouseButton:
|
||||
if event.button_index == MOUSE_BUTTON_LEFT:
|
||||
_resize_pressed = event.pressed
|
||||
if _resize_pressed:
|
||||
_pressed_size = _edit_box.size
|
||||
_pressed_pos = get_global_mouse_position()
|
||||
)
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
func popup(rect: Rect2 = Rect2()):
|
||||
if _edit_box == null: await ready
|
||||
|
||||
if rect.position != Vector2():
|
||||
_edit_box.global_position = rect.position
|
||||
if rect.size != Vector2():
|
||||
_edit_box.size = rect.size
|
||||
|
||||
# 聚焦编辑
|
||||
_edit_box.visible = true
|
||||
_edit_box.set_caret_line( _edit_box.get_line_count() )
|
||||
_edit_box.set_caret_column( _edit_box.text.length() )
|
||||
self.showed = true
|
||||
|
||||
# print("[ PopupEditBox ] 弹出窗口")
|
||||
|
||||
# 取消焦点时隐藏
|
||||
var t = _edit_box.text
|
||||
_edit_box.grab_focus()
|
||||
_edit_box.focus_exited.connect(func():
|
||||
if t != _edit_box.text:
|
||||
self.popup_hide.emit(_edit_box.text)
|
||||
_edit_box.visible = false
|
||||
# print("[ PopupEditBox ] 弹窗隐藏")
|
||||
, Object.CONNECT_ONE_SHOT)
|
||||
|
||||
|
||||
func _on_edit_box_resized():
|
||||
if _edit_box == null: await ready
|
||||
self.box_size_changed.emit(_edit_box.size)
|
||||
|
||||
|
||||
func _on_edit_box_gui_input(event):
|
||||
if event is InputEventKey:
|
||||
if event.pressed:
|
||||
if not event.alt_pressed:
|
||||
# Enter/Tab 切换单元格
|
||||
if event.keycode in [KEY_ENTER, KEY_KP_ENTER]:
|
||||
self.input_switch_char.emit(KEY_ENTER)
|
||||
get_tree().root.set_input_as_handled()
|
||||
|
||||
elif event.keycode in [KEY_TAB]:
|
||||
self.input_switch_char.emit(KEY_TAB)
|
||||
get_tree().root.set_input_as_handled()
|
||||
|
||||
else:
|
||||
# Alt+Enter换行
|
||||
if event.keycode in [KEY_ENTER, KEY_KP_ENTER]:
|
||||
_edit_box.insert_text_at_caret("\n")
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
[gd_scene load_steps=2 format=3 uid="uid://4xts0ha85fja"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/edit_box_window/edit_box_window.gd" id="1_qj0ea"]
|
||||
|
||||
[node name="popup_edit_box" type="Control"]
|
||||
visible = false
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
size_flags_horizontal = 0
|
||||
size_flags_vertical = 0
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_qj0ea")
|
||||
box_size = Vector2(200, 120)
|
||||
|
||||
[node name="edit_box" type="TextEdit" parent="."]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 30)
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 120.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
wrap_mode = 1
|
||||
|
||||
[node name="scale_rect" type="Control" parent="edit_box"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchor_left = 0.996
|
||||
anchor_top = 0.987
|
||||
anchor_right = 0.996
|
||||
anchor_bottom = 0.987
|
||||
offset_left = -0.200012
|
||||
offset_top = -0.440002
|
||||
offset_right = 7.79999
|
||||
offset_bottom = 7.56
|
||||
mouse_default_cursor_shape = 12
|
||||
|
||||
[connection signal="gui_input" from="edit_box" to="." method="_on_edit_box_gui_input"]
|
||||
[connection signal="resized" from="edit_box" to="." method="_on_edit_box_resized"]
|
|
@ -1,88 +0,0 @@
|
|||
#============================================================
|
||||
# Serial Number Container
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 23:09:40
|
||||
#============================================================
|
||||
# 显示左上的数字序号的容器
|
||||
@tool
|
||||
class_name SerialNumberContainer
|
||||
extends GridContainer
|
||||
|
||||
|
||||
## 显示序号的单元格场景
|
||||
@export var serial_number_cell : PackedScene
|
||||
|
||||
|
||||
var __init_node = InjectUtil.auto_inject(self, "_", true)
|
||||
|
||||
var _table_container : TableContainer
|
||||
var _h_serial_number_container : HBoxContainer
|
||||
var _v_serial_number_container : VBoxContainer
|
||||
var _space : Control
|
||||
|
||||
|
||||
var _last_top_left : Vector2i
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
## 更新横竖列数字
|
||||
##[br]
|
||||
##[br][code]top_left[/code] 以 top_left 值开始向下更新
|
||||
func update_serial_number(top_left: Vector2i):
|
||||
var serial_number : SerialNumberCell
|
||||
for i in _h_serial_number_container.get_child_count():
|
||||
serial_number = _h_serial_number_container.get_child(i) as SerialNumberCell
|
||||
serial_number.show_number(top_left.x + i)
|
||||
for i in _v_serial_number_container.get_child_count():
|
||||
serial_number = _v_serial_number_container.get_child(i) as SerialNumberCell
|
||||
serial_number.show_number(top_left.y + i)
|
||||
_last_top_left = top_left
|
||||
|
||||
_space.custom_minimum_size = Vector2(_v_serial_number_container.size.x, _h_serial_number_container.size.y)
|
||||
if _space.custom_minimum_size == Vector2(0,0):
|
||||
_space.custom_minimum_size = Vector2(27, 35)
|
||||
|
||||
## 更新这个行高
|
||||
func update_row_height(origin_row: int, height: int):
|
||||
if _v_serial_number_container.get_child_count() > 0:
|
||||
var node = _v_serial_number_container.get_child(origin_row) as Control
|
||||
node.custom_minimum_size.y = height
|
||||
|
||||
## 更新这个列宽
|
||||
func update_column_width(origin_column: int, width: int):
|
||||
if _h_serial_number_container.get_child_count() > 0:
|
||||
var node = _h_serial_number_container.get_child(origin_column) as Control
|
||||
node.custom_minimum_size.x = width
|
||||
|
||||
|
||||
## 更新行列数字标题节点的数量
|
||||
func update_grid_cell_count(grid_size: Vector2i):
|
||||
if _table_container == null:
|
||||
while _table_container == null:
|
||||
await get_tree().process_frame
|
||||
|
||||
# 表格数量发生改变时添加序号节点单元格
|
||||
var tile_size = _table_container.get_tile_size()
|
||||
# print_debug("单元格大小:", tile_size)
|
||||
|
||||
# 水平单元格
|
||||
if grid_size.x > _h_serial_number_container.get_child_count():
|
||||
var diff_count = grid_size.x - _h_serial_number_container.get_child_count()
|
||||
for i in diff_count:
|
||||
var node = serial_number_cell.instantiate() as Control
|
||||
node.custom_minimum_size = tile_size
|
||||
_h_serial_number_container.add_child(node)
|
||||
|
||||
# 垂直单元格
|
||||
if grid_size.y > _v_serial_number_container.get_child_count():
|
||||
var diff_count = grid_size.y - _v_serial_number_container.get_child_count()
|
||||
for i in diff_count:
|
||||
var node = serial_number_cell.instantiate() as Control
|
||||
node.custom_minimum_size.y = tile_size.y
|
||||
_v_serial_number_container.add_child(node)
|
||||
|
||||
update_serial_number(_last_top_left)
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#============================================================
|
||||
# Line
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 16:57:14
|
||||
#============================================================
|
||||
@tool
|
||||
class_name ColumnContainer
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
signal newly_added_cell(cell: Node)
|
||||
|
||||
|
||||
@export
|
||||
var item : PackedScene
|
||||
|
||||
|
||||
@onready
|
||||
var container := %container as HBoxContainer
|
||||
|
||||
|
||||
func update_cell_amount(count: int):
|
||||
if container == null:
|
||||
await self.ready
|
||||
if item:
|
||||
var node
|
||||
for i in count - container.get_child_count():
|
||||
node = item.instantiate()
|
||||
container.add_child(node)
|
||||
newly_added_cell.emit(node)
|
||||
|
||||
|
||||
func get_cells():
|
||||
return container.get_children()
|
||||
|
||||
|
||||
func get_cell(idx: int) -> Node:
|
||||
return container.get_child(idx)
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
[gd_scene load_steps=2 format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_container/column_container/column_container.gd" id="1_1xecd"]
|
||||
|
||||
[node name="column_container" type="MarginContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_1xecd")
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="container" type="HBoxContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 648.0
|
||||
theme_override_constants/separation = 0
|
|
@ -1,36 +0,0 @@
|
|||
#============================================================
|
||||
# List
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 16:57:09
|
||||
#============================================================
|
||||
## 数据行的容器
|
||||
|
||||
@tool
|
||||
class_name RowContainer
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
signal newly_added_line(line: ColumnContainer)
|
||||
|
||||
|
||||
@export var item : PackedScene
|
||||
|
||||
|
||||
@onready var container = %container as VBoxContainer
|
||||
|
||||
|
||||
func get_columns_containers() -> Array:
|
||||
return container.get_children()
|
||||
|
||||
|
||||
## 更新行数量
|
||||
func update_row_amount(count: int):
|
||||
if container == null:
|
||||
await self.ready
|
||||
var node
|
||||
for i in count - container.get_child_count():
|
||||
node = item.instantiate()
|
||||
container.add_child(node)
|
||||
newly_added_line.emit(node)
|
||||
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
[gd_scene load_steps=3 format=3 uid="uid://dyhbjld6bhk1p"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_container/row_container/row_container.gd" id="1_hd2dr"]
|
||||
[ext_resource type="PackedScene" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_container/column_container/column_container.tscn" id="2_cbcfu"]
|
||||
|
||||
[node name="row_container" type="MarginContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 1.0
|
||||
offset_right = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_hd2dr")
|
||||
item = ExtResource("2_cbcfu")
|
||||
|
||||
[node name="container" type="VBoxContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 0
|
|
@ -1,147 +0,0 @@
|
|||
#============================================================
|
||||
# Table Container
|
||||
#============================================================
|
||||
# - datetime: 2022-11-26 17:01:54
|
||||
#============================================================
|
||||
## 表格
|
||||
##
|
||||
##这里只进行表格单元格相关管理,不进行数据的处理,仅作为表格样式显示
|
||||
@tool
|
||||
class_name TableContainer
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
## 新增单元格
|
||||
signal newly_added_cell(coords: Vector2i, new_cell: Control)
|
||||
## 网格中的单元格大小发生改变
|
||||
signal grid_cell_size_changed(grid_size: Vector2i)
|
||||
|
||||
|
||||
@export var cell : PackedScene
|
||||
|
||||
|
||||
var __init_node = InjectUtil.auto_inject(self, "_", true)
|
||||
|
||||
var _row_container : RowContainer
|
||||
var _update_row_column_amount_timer : Timer
|
||||
|
||||
|
||||
# 表格单元格数量大小
|
||||
var _grid_cell_count_size := Vector2i()
|
||||
# 单个单元格大小
|
||||
var _tile_size := Vector2i()
|
||||
# 单元格列表
|
||||
var _cell_list : Array[Control] = []
|
||||
# 列对应的单元格
|
||||
var _column_to_cells_map := {}
|
||||
# 行对应的单元格
|
||||
var _row_to_cells_map := {}
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
## 获取单元格行列数量大小
|
||||
func get_grid_row_column_count_size() -> Vector2i:
|
||||
return _grid_cell_count_size
|
||||
|
||||
## 获取表格的行数量
|
||||
func get_grid_row_count() -> int:
|
||||
return _grid_cell_count_size.y
|
||||
|
||||
## 获取表格的列数量
|
||||
func get_grid_column_count() -> int:
|
||||
return _grid_cell_count_size.x
|
||||
|
||||
## 获取单元格大小
|
||||
func get_tile_size() -> Vector2i:
|
||||
return _tile_size
|
||||
|
||||
## 获取行容器
|
||||
func get_row_container() -> RowContainer:
|
||||
return _row_container
|
||||
|
||||
## 获取所有行的单元格列容器
|
||||
func get_column_container() -> Array[ColumnContainer]:
|
||||
return _row_container.get_columns_containers()
|
||||
|
||||
## 获取所有单元格
|
||||
func get_all_cell() -> Array[Control]:
|
||||
return _cell_list
|
||||
|
||||
## 获取一列的单元格。column 从 0 开始,最大为 [method get_grid_row_column_count_siz] 的 x 值
|
||||
func get_column_cells(column: int) -> Array:
|
||||
return _column_to_cells_map[column]
|
||||
|
||||
## 获取这一行的单元格。row 从 0 开始,最大为 [method get_grid_row_column_count_siz] 的 y 值
|
||||
func get_row_cells(row: int) -> Array:
|
||||
return _row_to_cells_map[row]
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _ready() -> void:
|
||||
assert(cell != null, "还没有设置 cell 属性!")
|
||||
|
||||
# cell 的更新与大小
|
||||
_row_container.newly_added_line.connect(func(new_line: ColumnContainer):
|
||||
new_line.newly_added_cell.connect( func(new_cell: Control):
|
||||
self._cell_list.append(new_cell)
|
||||
|
||||
var column : int = new_cell.get_index()
|
||||
var row : int = new_line.get_index()
|
||||
self.newly_added_cell.emit( Vector2i(column, row), new_cell )
|
||||
|
||||
if _column_to_cells_map.has(column):
|
||||
_column_to_cells_map[column].append(new_cell)
|
||||
else:
|
||||
_column_to_cells_map[column] = []
|
||||
_column_to_cells_map[column].append(new_cell)
|
||||
if _row_to_cells_map.has(row):
|
||||
_row_to_cells_map[row].append(new_cell)
|
||||
else:
|
||||
_row_to_cells_map[row] = []
|
||||
_row_to_cells_map[row].append(new_cell)
|
||||
)
|
||||
new_line.item = self.cell
|
||||
new_line.update_cell_amount(_grid_cell_count_size.x)
|
||||
)
|
||||
self.resized.connect(_update_row_column_amount)
|
||||
|
||||
# 先创建出来第一个,用以获取最小 cell 大小
|
||||
_row_container.update_row_amount(1)
|
||||
(_row_container.get_columns_containers()[0] as ColumnContainer).update_cell_amount(1)
|
||||
var first_cell = _row_container.get_columns_containers()[0].get_cells()[0] as Control
|
||||
_tile_size = first_cell.size
|
||||
|
||||
# 更新单元格数量
|
||||
_update_row_column_amount_timer.timeout.connect(func():
|
||||
var tmp_cell_amount = _grid_cell_count_size
|
||||
_grid_cell_count_size.x = int(self.size.x / _tile_size.x) + 1
|
||||
_grid_cell_count_size.y = int(self.size.y / _tile_size.y) + 1
|
||||
|
||||
if tmp_cell_amount != _grid_cell_count_size:
|
||||
print("[ TableContainer ] 单元格小:", _grid_cell_count_size)
|
||||
self.grid_cell_size_changed.emit(_grid_cell_count_size)
|
||||
|
||||
for line in _row_container.get_columns_containers():
|
||||
line = line as ColumnContainer
|
||||
line.update_cell_amount(_grid_cell_count_size.x)
|
||||
_row_container.update_row_amount(_grid_cell_count_size.y)
|
||||
)
|
||||
_update_row_column_amount_timer.start()
|
||||
_update_row_column_amount_timer.autostart = true
|
||||
_update_row_column_amount_timer.one_shot = true
|
||||
_update_row_column_amount_timer.timeout.emit()
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
func _update_row_column_amount():
|
||||
_update_row_column_amount_timer.stop()
|
||||
_update_row_column_amount_timer.start()
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://d02f0rqt6qnpv"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_container/table_container.gd" id="1_v7wlj"]
|
||||
[ext_resource type="PackedScene" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/input_cell/input_cell.tscn" id="2_t8gur"]
|
||||
[ext_resource type="PackedScene" uid="uid://dyhbjld6bhk1p" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_container/row_container/row_container.tscn" id="3_auuku"]
|
||||
|
||||
[node name="table_container" type="MarginContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_v7wlj")
|
||||
cell = ExtResource("2_t8gur")
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="control" type="Control" parent="."]
|
||||
unique_name_in_owner = true
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="row_container" parent="control" instance=ExtResource("3_auuku")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 0.0
|
||||
offset_right = 0.0
|
||||
grow_horizontal = 1
|
||||
grow_vertical = 1
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="update_row_column_amount_timer" type="Timer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
wait_time = 0.05
|
||||
one_shot = true
|
||||
autostart = true
|
|
@ -1,105 +0,0 @@
|
|||
#============================================================
|
||||
# Table Data Set
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2023-05-16 23:22:41
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
## 表格数据集
|
||||
##
|
||||
##专门存储管理表单的数据。处理每个单元格中的数据
|
||||
class_name TableDataEditor_TableDataSet
|
||||
|
||||
|
||||
enum {
|
||||
COLUMN,
|
||||
ROW,
|
||||
}
|
||||
|
||||
# 表格数据。以 [code]grid_data[行][列] = 值[/code] 的格式存储数据。
|
||||
var grid_data : Dictionary = {}
|
||||
# 列集合。所有行哪些列有值
|
||||
var column_set : Dictionary = {}
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
func get_config_data():
|
||||
return {
|
||||
"grid_data": grid_data,
|
||||
"column_set": column_set,
|
||||
}
|
||||
|
||||
func set_config_data(data: Dictionary):
|
||||
self.grid_data = data.get("grid_data", {})
|
||||
self.column_set = data.get("column_set", {})
|
||||
|
||||
func get_origin_data() -> Dictionary:
|
||||
return grid_data
|
||||
|
||||
func is_empty() -> bool:
|
||||
return grid_data.is_empty()
|
||||
|
||||
func has_value(coords: Vector2i) -> bool:
|
||||
return (grid_data.has(coords[ROW])
|
||||
and grid_data[coords[ROW]].has(coords[COLUMN])
|
||||
)
|
||||
|
||||
func get_value(coords: Vector2i):
|
||||
if has_value(coords):
|
||||
var row : int = coords[ROW]
|
||||
var column : int = coords[COLUMN]
|
||||
return grid_data[row][column]
|
||||
return ""
|
||||
|
||||
func set_value(coords: Vector2i, value) -> void:
|
||||
var row : int = coords[ROW]
|
||||
var column : int = coords[COLUMN]
|
||||
|
||||
if not grid_data.has(row):
|
||||
grid_data[row] = {}
|
||||
grid_data[row][column] = value
|
||||
|
||||
if not column_set.has(column):
|
||||
column_set[column] = 0
|
||||
column_set[column] += 1
|
||||
|
||||
|
||||
func get_max_column() -> int:
|
||||
if column_set.is_empty():
|
||||
return 0
|
||||
return column_set.keys().max()
|
||||
|
||||
func remove_value(coords: Vector2i) -> bool:
|
||||
if has_value(coords):
|
||||
var row : int = coords[ROW]
|
||||
var column : int = coords[COLUMN]
|
||||
grid_data[row].erase(column)
|
||||
column_set[column] -= 1
|
||||
|
||||
# 没有数据时,进行移除
|
||||
if Dictionary(grid_data[row]).is_empty():
|
||||
grid_data.erase(row)
|
||||
if column_set[column] == 0:
|
||||
column_set.erase(column)
|
||||
return true
|
||||
return false
|
||||
|
||||
func get_row_list() -> Array[int]:
|
||||
return Array(grid_data.keys(), TYPE_INT, "", null)
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _to_string():
|
||||
return var_to_str( TableDataUtil.Classes.get_dict_by_property(self) )
|
||||
|
||||
|
||||
func _init(data: Dictionary = {}):
|
||||
self.grid_data = data.get("grid_data", {})
|
||||
self.column_set = data.get("column_set", {})
|
||||
|
||||
|
|
@ -1,498 +0,0 @@
|
|||
#============================================================
|
||||
# Table Edit
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2022-11-26 17:53:52
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
## 编辑网格
|
||||
##
|
||||
##这里管理 Cell 单元格的数据内容,真正开始对表格数据进行处理。
|
||||
@tool
|
||||
class_name TableEdit
|
||||
extends MarginContainer
|
||||
|
||||
|
||||
## 数据发生改变
|
||||
signal data_set_changed
|
||||
## 选中单元格
|
||||
signal selected_cell(cell: InputCell)
|
||||
## 取消选中单元格
|
||||
signal deselected_cell(cell: InputCell)
|
||||
## 单击单元格
|
||||
signal single_clicked_cell(cell: InputCell)
|
||||
## 双击单元格
|
||||
signal double_clicked_cell(cell: InputCell)
|
||||
## 准备编辑单元格
|
||||
signal ready_edit_cell(cell: InputCell)
|
||||
## 已编辑单元格
|
||||
signal edited_cell(cell: InputCell)
|
||||
## 单元格数据发生改变
|
||||
signal cell_value_changed(cell: InputCell, coords: Vector2i, previous: String, value: String)
|
||||
## 滚动条滚动
|
||||
signal scroll_changed(coords: Vector2i)
|
||||
## 行高发生改变
|
||||
signal row_height_changed(value: int)
|
||||
## 列宽发生改变
|
||||
signal column_width_changed(value: int)
|
||||
## 弹窗编辑器表格大小发生改变
|
||||
signal popup_edit_box_size_changed(box_size: Vector2)
|
||||
|
||||
|
||||
## 双击单元格进行编辑
|
||||
@export var double_click_edit : bool = true
|
||||
|
||||
|
||||
## 表格中的数据。格式:[code]data[row][column] = data[/code]
|
||||
var grid_data := {}:
|
||||
set(v):
|
||||
grid_data = v
|
||||
|
||||
update_cell_list()
|
||||
scroll_to(Vector2i(0,0))
|
||||
self.data_set_changed.emit()
|
||||
## 默认单元格大小
|
||||
var default_tile_size : Vector2i
|
||||
## 数据集,管理获取数据
|
||||
var data_set : TableDataEditor_TableDataSet = TableDataEditor_TableDataSet.new():
|
||||
set(v):
|
||||
data_set = v
|
||||
|
||||
# 当前线程其他代码调用完成后调用这个
|
||||
(func():
|
||||
update_cell_list()
|
||||
scroll_to(Vector2i(0,0))
|
||||
).call_deferred()
|
||||
## 行对应的行高
|
||||
var row_to_height_map := {}
|
||||
## 列对应的列宽
|
||||
var column_to_width_map := {}
|
||||
|
||||
|
||||
var __init_node = InjectUtil.auto_inject(self, "_", true)
|
||||
|
||||
var _table_container : TableContainer
|
||||
var _popup_edit_box : PopupEditBox
|
||||
var _v_scroll_bar : VScrollBar
|
||||
var _h_scroll_bar : HScrollBar
|
||||
var _update_grid_data_timer : Timer
|
||||
var _serial_number_container : SerialNumberContainer
|
||||
|
||||
|
||||
# 是否允许发出取消选中 cell 的信号,用于编辑表格数据,编辑的时候代表这个单元格还是被选中的
|
||||
var _enabled_emit_deselected_signal := true
|
||||
|
||||
# 原始坐标位置对应的单元格
|
||||
var _origin_coords_to_cell_map := {}
|
||||
# 单元格对应的原点坐标位置
|
||||
var _cell_to_origin_coords_map := {}
|
||||
# 当前选中的单元格
|
||||
var _selected_cell : InputCell:
|
||||
set(v):
|
||||
_selected_cell = v
|
||||
if v != null:
|
||||
_last_cell = v
|
||||
# 最后一次选中的单元格
|
||||
var _last_cell : InputCell
|
||||
# 上一次左上角的坐标位置
|
||||
var _latest_top_left := Vector2i()
|
||||
|
||||
# 正在按着 alt 键
|
||||
var _pressing_alt := false
|
||||
# 是否已经开始更新
|
||||
var _updated : bool = false
|
||||
|
||||
|
||||
#============================================================
|
||||
# SetGet
|
||||
#============================================================
|
||||
## 获取编辑弹窗
|
||||
func get_edit_dialog() -> PopupEditBox:
|
||||
return _popup_edit_box
|
||||
|
||||
func get_grid_data() -> Dictionary:
|
||||
return grid_data
|
||||
|
||||
func get_data_set() -> TableDataEditor_TableDataSet:
|
||||
return data_set
|
||||
|
||||
## 获取当前滚动到的左上角位置
|
||||
func get_scroll_top_left() -> Vector2i:
|
||||
return Vector2i( _h_scroll_bar.value, _v_scroll_bar.value )
|
||||
|
||||
## 获取滚动条最顶部 Y 的值
|
||||
func get_scroll_top() -> int:
|
||||
return int(_v_scroll_bar.value)
|
||||
|
||||
## 获取滚动条最左边 X 的值
|
||||
func get_scroll_left() -> int:
|
||||
return int(_h_scroll_bar.value)
|
||||
|
||||
## 获取这个 cell 的当前坐标位置
|
||||
func get_cell_coords(cell: InputCell) -> Vector2i:
|
||||
return get_scroll_top_left() + _cell_to_origin_coords_map.get(cell, Vector2i(-1, -1))
|
||||
|
||||
## 获取这个坐标上的 cell
|
||||
func get_cell_node(coords: Vector2i) -> InputCell:
|
||||
var origin_coords = coords - get_scroll_top_left()
|
||||
return _origin_coords_to_cell_map.get(origin_coords) as InputCell
|
||||
|
||||
## 获取列宽
|
||||
func get_column_width(column: int, default_width: int = 0) -> int:
|
||||
if default_width <= 0:
|
||||
default_width = _table_container.get_tile_size().x
|
||||
return column_to_width_map.get(column, default_width)
|
||||
|
||||
## 获取行高
|
||||
func get_row_height(row: int, default_heigt : int = 0):
|
||||
if default_heigt <= 0:
|
||||
default_heigt = _table_container.get_tile_size().y
|
||||
return row_to_height_map.get(row, default_heigt)
|
||||
|
||||
## 获取列宽数据,数据中的 key 为列值,对应列宽
|
||||
func get_column_width_data() -> Dictionary:
|
||||
return column_to_width_map
|
||||
|
||||
## 获取行高数据,数据中的 key 为行值,对应行宽
|
||||
func get_row_height_data() -> Dictionary:
|
||||
return row_to_height_map
|
||||
|
||||
## 获取单元格行列大小数量
|
||||
func get_column_row_size() -> Vector2i:
|
||||
return _table_container.get_grid_row_column_count_size()
|
||||
|
||||
## 获取单元格当前整个矩形数据的位置
|
||||
func get_current_rect() -> Rect2i:
|
||||
return Rect2i(get_scroll_top_left(), get_column_row_size())
|
||||
|
||||
|
||||
#============================================================
|
||||
# 内置
|
||||
#============================================================
|
||||
func _ready() -> void:
|
||||
|
||||
# 滚动条
|
||||
_h_scroll_bar.scrolling.connect(func():
|
||||
_h_scroll_bar.max_value = _h_scroll_bar.value + 100
|
||||
update_cell_list()
|
||||
)
|
||||
_v_scroll_bar.scrolling.connect(func():
|
||||
_v_scroll_bar.max_value = _v_scroll_bar.value + 100
|
||||
update_cell_list()
|
||||
)
|
||||
|
||||
# 编辑表格窗。隐藏就更新对应的单元格的数据
|
||||
_popup_edit_box.popup_hide.connect(func(value):
|
||||
# 弹窗消失后才允许发送取消选中的信号
|
||||
_enabled_emit_deselected_signal = true
|
||||
# 更新选中的 cell
|
||||
if _last_cell:
|
||||
_last_cell.set_value( value )
|
||||
var coords = get_cell_coords(_last_cell)
|
||||
alter_value(coords, value)
|
||||
self.edited_cell.emit(_last_cell)
|
||||
|
||||
)
|
||||
|
||||
# 必须要等空闲时间时调用,否则 _table_container 中的节点没有加载完成,则看不到节点的大小
|
||||
update_serial_num.call_deferred(Vector2i(0, 0))
|
||||
self.default_tile_size = _table_container.get_tile_size()
|
||||
|
||||
# 切换窗口时取消 alt
|
||||
while get_window() == null:
|
||||
await Engine.get_main_loop().process_frame
|
||||
_pressing_alt = false
|
||||
|
||||
# 更新表格数据
|
||||
update_cell_list()
|
||||
|
||||
_serial_number_container.update_serial_number(Vector2i(0,0))
|
||||
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_WM_WINDOW_FOCUS_OUT:
|
||||
_pressing_alt = false
|
||||
|
||||
|
||||
func _unhandled_input(event):
|
||||
if event is InputEventKey:
|
||||
# 按下 alt 键
|
||||
if event.keycode == KEY_ALT:
|
||||
# if not _pressing_alt and event.is_pressed():
|
||||
# print("[ TableEdit ] 按下了 Alt 键")
|
||||
_pressing_alt = event.is_pressed()
|
||||
|
||||
|
||||
#============================================================
|
||||
# 自定义
|
||||
#============================================================
|
||||
# 真正进行修改行高,但数据不会缓存到数据中
|
||||
func _alter_row_height(row: int, height: int):
|
||||
height = max(height, default_tile_size.y)
|
||||
for cell in _table_container.get_row_cells(row - get_scroll_top()):
|
||||
cell.custom_minimum_size.y = height
|
||||
_serial_number_container.update_row_height(row - get_scroll_top(), height)
|
||||
|
||||
# 真正进行修改单元格宽度,但数据不会缓存到数据中
|
||||
func _alter_column_width(column: int, width: int):
|
||||
width = max(width, default_tile_size.x)
|
||||
for cell in _table_container.get_column_cells( column - get_scroll_left() ):
|
||||
cell.custom_minimum_size.x = width
|
||||
_serial_number_container.update_column_width(column - get_scroll_left(), width)
|
||||
|
||||
|
||||
## 修改单元格数据
|
||||
##[br]
|
||||
##[br][code]coords[/code] 修改的坐标位置
|
||||
##[br][code]value[/code] 修改的值,如果为需改为 [code]""[/code],则会删除掉这个数据
|
||||
##[br][code]emit_signal_state[/code] 是否发送信号
|
||||
func alter_value(
|
||||
coords: Vector2i,
|
||||
value: String,
|
||||
emit_signal_state: bool = true
|
||||
) -> void:
|
||||
var previous = data_set.get_value(coords)
|
||||
if value:
|
||||
if previous != value:
|
||||
data_set.set_value(coords, value)
|
||||
if emit_signal_state:
|
||||
var cell = get_cell_node(coords)
|
||||
self.cell_value_changed.emit(cell, coords, previous, value )
|
||||
else:
|
||||
if data_set.remove_value(coords):
|
||||
if emit_signal_state:
|
||||
var cell = get_cell_node(coords)
|
||||
self.cell_value_changed.emit(cell, coords, previous, "")
|
||||
|
||||
|
||||
## 修改行高
|
||||
##[br]
|
||||
##[br][code]row[/code] 所在的行,从 1 开始
|
||||
##[br][code]height[/code] 设置的行高
|
||||
func alter_row_height(row: int, height: int):
|
||||
row_to_height_map[row] = height
|
||||
_alter_row_height(row, height)
|
||||
|
||||
|
||||
## 修改列宽
|
||||
##[br]
|
||||
##[br][code]column[/code] 所在的列,从 1 开始
|
||||
##[br][code]width[/code] 设置的列宽
|
||||
func alter_column_width(column: int, width: int):
|
||||
column_to_width_map[column] = width
|
||||
_alter_column_width(column, width)
|
||||
|
||||
|
||||
## 更新单元格信息(使用计时器缓冲更新,防止同一时间多次重复调用)
|
||||
func update_cell_list():
|
||||
if _updated:
|
||||
return
|
||||
_updated = true
|
||||
await Engine.get_main_loop().process_frame
|
||||
_updated = false
|
||||
force_update_cell_list()
|
||||
|
||||
|
||||
# 真正实际执行的更新
|
||||
func force_update_cell_list():
|
||||
# 更新数据
|
||||
var top_left = get_scroll_top_left()
|
||||
var coords : Vector2i
|
||||
for cell in _cell_to_origin_coords_map:
|
||||
coords = get_cell_coords(cell)
|
||||
cell.show_value(data_set.get_value(coords))
|
||||
|
||||
# 更新单元格的宽高
|
||||
var grid_row_column_size = _table_container.get_grid_row_column_count_size()
|
||||
for column in range(top_left.x, top_left.x + grid_row_column_size.x):
|
||||
_alter_column_width(column, get_column_width(column, 0) )
|
||||
for row in range(top_left.y, top_left.y + grid_row_column_size.y):
|
||||
_alter_row_height(row, get_row_height(row, 0) )
|
||||
|
||||
|
||||
if _latest_top_left != top_left:
|
||||
_latest_top_left = top_left
|
||||
self.scroll_changed.emit( _latest_top_left )
|
||||
_popup_edit_box.showed = false
|
||||
_popup_edit_box.get_edit_box().visible = false
|
||||
_serial_number_container.update_serial_number(top_left)
|
||||
|
||||
|
||||
## 更新行列序号的值
|
||||
##[br]
|
||||
##[br][code]left_top[/code] 左上角行列值
|
||||
func update_serial_num(left_top: Vector2i):
|
||||
_serial_number_container.update_column_width(left_top.x, default_tile_size.x)
|
||||
_serial_number_container.update_row_height(left_top.y, default_tile_size.y)
|
||||
|
||||
|
||||
## 滚动到指定位置
|
||||
func scroll_to(left_top: Vector2i):
|
||||
_h_scroll_bar.value = left_top.x
|
||||
_v_scroll_bar.value = left_top.y
|
||||
_h_scroll_bar.scrolling.emit()
|
||||
_v_scroll_bar.scrolling.emit()
|
||||
|
||||
|
||||
## 编辑单元格
|
||||
func edit_cell(cell_coords: Vector2i):
|
||||
_enabled_emit_deselected_signal = false
|
||||
|
||||
# 设置选中的 cell
|
||||
var cell = get_cell_node(cell_coords)
|
||||
_selected_cell = cell
|
||||
|
||||
# 弹窗
|
||||
_popup_edit_box.text = data_set.get_value(cell_coords)
|
||||
_popup_edit_box.popup(Rect2(cell.global_position, Vector2(0,0)))
|
||||
self.ready_edit_cell.emit(cell)
|
||||
|
||||
|
||||
## 切换到下一个位置进行编辑。表格坐标是从 Vector2i(1, 1) 开始的,不是 Vector2i(0, 0)
|
||||
func edit_to_next_cell(coords: Vector2i, direction : Vector2i):
|
||||
if _selected_cell:
|
||||
var next_coords = coords + direction
|
||||
next_coords.x = max(1, next_coords.x)
|
||||
next_coords.y = max(1, next_coords.y)
|
||||
|
||||
_popup_edit_box.showed = false
|
||||
_popup_edit_box.showed = true
|
||||
|
||||
# 如果所在的单元格超出当前视图内的单元格,则进行滚动
|
||||
if direction.x > 0 or direction.y > 0:
|
||||
var get_width_height_callback : Callable = self.get_column_width \
|
||||
if direction.x != 0 \
|
||||
else self.get_row_height
|
||||
var dir_idx = 0 \
|
||||
if direction.x != 0 \
|
||||
else 1
|
||||
var total = 0
|
||||
var top_left = get_scroll_top_left()
|
||||
for i in range(next_coords[dir_idx] - 1, top_left[dir_idx] - 1, -1):
|
||||
total += abs(get_width_height_callback.call(i)) + 8
|
||||
if total > self.size[dir_idx]:
|
||||
# 超出屏幕则换行
|
||||
scroll_to(top_left + direction * (i - top_left[dir_idx] + 1))
|
||||
break
|
||||
|
||||
else:
|
||||
var rect = get_current_rect()
|
||||
rect.size -= Vector2i.ONE
|
||||
if not rect.has_point(next_coords):
|
||||
var top_left = get_scroll_top_left()
|
||||
scroll_to(top_left + direction)
|
||||
|
||||
# 切换到下一个网格的位置编辑网格
|
||||
await Engine.get_main_loop().create_timer(0.1).timeout
|
||||
edit_cell.call_deferred(next_coords)
|
||||
|
||||
|
||||
|
||||
#============================================================
|
||||
# 连接信号
|
||||
#============================================================
|
||||
# 添加新的单元格时
|
||||
func _newly_added_cell(coords: Vector2i, new_cell: InputCell):
|
||||
# 滑轮滚动
|
||||
new_cell.gui_input.connect(func(event):
|
||||
# 单元格 Input
|
||||
if event is InputEventMouseButton and event.is_pressed():
|
||||
var scroll_bar : ScrollBar
|
||||
if _pressing_alt:
|
||||
scroll_bar = _h_scroll_bar
|
||||
else:
|
||||
scroll_bar = _v_scroll_bar
|
||||
if event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||
scroll_bar.value += scroll_bar.step
|
||||
scroll_bar.scrolling.emit()
|
||||
elif event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||
scroll_bar.value -= scroll_bar.step
|
||||
scroll_bar.scrolling.emit()
|
||||
)
|
||||
|
||||
# 单元格行列坐标映射
|
||||
_cell_to_origin_coords_map[new_cell] = coords
|
||||
_origin_coords_to_cell_map[coords] = new_cell
|
||||
|
||||
update_cell_list()
|
||||
|
||||
# 选中单元格
|
||||
new_cell.focus_entered.connect(func():
|
||||
_selected_cell = new_cell
|
||||
if _popup_edit_box.showed:
|
||||
_popup_edit_box.showed = false
|
||||
if _selected_cell:
|
||||
# 取消上次选中的单元格
|
||||
self.deselected_cell.emit(_selected_cell)
|
||||
_selected_cell = null
|
||||
|
||||
# 当前 cell
|
||||
self.selected_cell.emit(new_cell)
|
||||
_popup_edit_box.showed = false
|
||||
)
|
||||
|
||||
# 取消选中单元格
|
||||
new_cell.focus_exited.connect(func():
|
||||
if _enabled_emit_deselected_signal:
|
||||
_selected_cell = null
|
||||
self.deselected_cell.emit(new_cell)
|
||||
)
|
||||
|
||||
# 单击
|
||||
new_cell.single_clicked.connect(func():
|
||||
if not double_click_edit:
|
||||
edit_cell(get_cell_coords(new_cell))
|
||||
self.single_clicked_cell.emit(new_cell)
|
||||
)
|
||||
|
||||
# 双击
|
||||
new_cell.double_clicked.connect(func():
|
||||
if double_click_edit:
|
||||
edit_cell(get_cell_coords(new_cell))
|
||||
self.double_clicked_cell.emit(new_cell)
|
||||
)
|
||||
|
||||
# 水平拖拽移动
|
||||
new_cell.h_dragged.connect(func(distance: float, pressed_node_size: Vector2i):
|
||||
# 表格当前坐标位置
|
||||
var current_coords = get_cell_coords(new_cell)
|
||||
var width = pressed_node_size.x + int(distance)
|
||||
alter_column_width(current_coords.x, width)
|
||||
|
||||
# 记录改变的列宽
|
||||
column_to_width_map[current_coords.x] = width
|
||||
self.column_width_changed.emit(width)
|
||||
)
|
||||
|
||||
# 垂直拖拽移动
|
||||
new_cell.v_dragged.connect(func(distance: float, pressed_node_size: Vector2i):
|
||||
|
||||
# 表格当前坐标位置
|
||||
var current_coords = get_cell_coords(new_cell)
|
||||
var height = pressed_node_size.y + int(distance)
|
||||
alter_row_height(current_coords.y, height)
|
||||
|
||||
# 记录改变的行高
|
||||
row_to_height_map[current_coords.y] = height
|
||||
self.row_height_changed.emit(height)
|
||||
|
||||
)
|
||||
|
||||
func _on_table_container_grid_cell_size_changed(grid_size):
|
||||
# 更新序号
|
||||
_serial_number_container.update_grid_cell_count(grid_size)
|
||||
|
||||
func _on_popup_edit_box_box_size_changed(box_size):
|
||||
self.popup_edit_box_size_changed.emit(box_size)
|
||||
|
||||
|
||||
func _on_popup_edit_box_input_switch_char(character):
|
||||
match character:
|
||||
KEY_TAB:
|
||||
var coords = get_cell_coords(_selected_cell)
|
||||
edit_to_next_cell(coords, Vector2i.LEFT if Input.is_key_pressed(KEY_SHIFT) else Vector2i.RIGHT)
|
||||
|
||||
KEY_ENTER:
|
||||
var coords = get_cell_coords(_selected_cell)
|
||||
edit_to_next_cell(coords, Vector2i.UP if Input.is_key_pressed(KEY_SHIFT) else Vector2i.DOWN)
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
[gd_scene load_steps=6 format=3 uid="uid://ctppgkl2dpksd"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_edit.gd" id="1_b56jx"]
|
||||
[ext_resource type="Script" path="res://addons/table_data_editor/src/table_data_editor/table_edit/serial_number_container.gd" id="2_irx05"]
|
||||
[ext_resource type="PackedScene" path="res://addons/table_data_editor/src/table_data_editor/table_edit/cell/serial_number_cell/serial_number_cell.tscn" id="3_xe81h"]
|
||||
[ext_resource type="PackedScene" uid="uid://d02f0rqt6qnpv" path="res://addons/table_data_editor/src/table_data_editor/table_edit/table_container/table_container.tscn" id="4_sm235"]
|
||||
[ext_resource type="PackedScene" uid="uid://4xts0ha85fja" path="res://addons/table_data_editor/src/table_data_editor/table_edit/edit_box_window/edit_box_window.tscn" id="6_mt1mb"]
|
||||
|
||||
[node name="table_edit" type="MarginContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 2
|
||||
theme_override_constants/margin_top = 2
|
||||
theme_override_constants/margin_right = 2
|
||||
theme_override_constants/margin_bottom = 2
|
||||
script = ExtResource("1_b56jx")
|
||||
|
||||
[node name="serial_number_container" type="GridContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 0
|
||||
theme_override_constants/v_separation = 0
|
||||
columns = 2
|
||||
script = ExtResource("2_irx05")
|
||||
serial_number_cell = ExtResource("3_xe81h")
|
||||
|
||||
[node name="space" type="Control" parent="serial_number_container"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(27, 35)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Control" type="Control" parent="serial_number_container"]
|
||||
clip_contents = true
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="h_serial_number_container" type="HBoxContainer" parent="serial_number_container/Control"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Control2" type="Control" parent="serial_number_container"]
|
||||
clip_contents = true
|
||||
custom_minimum_size = Vector2(32, 0)
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="v_serial_number_container" type="VBoxContainer" parent="serial_number_container/Control2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
theme_override_constants/separation = 0
|
||||
|
||||
[node name="Control3" type="Control" parent="serial_number_container"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="grid_container" type="GridContainer" parent="serial_number_container/Control3"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/h_separation = 0
|
||||
theme_override_constants/v_separation = 0
|
||||
columns = 2
|
||||
|
||||
[node name="table_container" parent="serial_number_container/Control3/grid_container" instance=ExtResource("4_sm235")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="v_scroll_bar" type="VScrollBar" parent="serial_number_container/Control3/grid_container"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 1
|
||||
min_value = 1.0
|
||||
step = 1.0
|
||||
page = 10.0
|
||||
value = 1.0
|
||||
exp_edit = true
|
||||
|
||||
[node name="h_scroll_bar" type="HScrollBar" parent="serial_number_container/Control3/grid_container"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 1
|
||||
min_value = 1.0
|
||||
step = 1.0
|
||||
page = 10.0
|
||||
value = 1.0
|
||||
exp_edit = true
|
||||
|
||||
[node name="Control" type="Control" parent="serial_number_container/Control3/grid_container"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="update_grid_data_timer" type="Timer" parent="serial_number_container/Control3/grid_container"]
|
||||
unique_name_in_owner = true
|
||||
wait_time = 0.02
|
||||
one_shot = true
|
||||
autostart = true
|
||||
|
||||
[node name="popup_edit_box" parent="." instance=ExtResource("6_mt1mb")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
|
||||
[connection signal="grid_cell_size_changed" from="serial_number_container/Control3/grid_container/table_container" to="." method="_on_table_container_grid_cell_size_changed"]
|
||||
[connection signal="newly_added_cell" from="serial_number_container/Control3/grid_container/table_container" to="." method="_newly_added_cell"]
|
||||
[connection signal="box_size_changed" from="popup_edit_box" to="." method="_on_popup_edit_box_box_size_changed"]
|
||||
[connection signal="input_switch_char" from="popup_edit_box" to="." method="_on_popup_edit_box_input_switch_char"]
|
|
@ -1,45 +0,0 @@
|
|||
#============================================================
|
||||
# Inject Util
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2023-05-14 23:53:46
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
## 注入节点工具
|
||||
class_name InjectUtil
|
||||
|
||||
|
||||
## 自动注入 unique (唯一名称)节点属性
|
||||
##[br]
|
||||
##[br][code]parent[/code] 目标节点,对这个节点的属性进行自动注入节点属性
|
||||
##[br][code]prefix[/code] 注入的属性的前缀值
|
||||
##[br]示例:
|
||||
##[codeblock]
|
||||
##extends Node
|
||||
##
|
||||
##var __init_node__ = InjectUtil.auto_inject(self, "_")
|
||||
### 当前场景中有 %sprite 、%collision 节点则会自动获取并自动设置下面两个属性
|
||||
##var _sprite : Sprite2D
|
||||
##var _collision: Collision
|
||||
##
|
||||
##[/codeblock]
|
||||
static func auto_inject(parent: Node, prefix: String = "", open_err: bool = false):
|
||||
var method : Callable = func():
|
||||
for data in (parent.get_script() as GDScript).get_script_property_list():
|
||||
if data['type'] == TYPE_OBJECT and parent[data['name']] == null:
|
||||
var prop = str(data['name']).trim_prefix(prefix)
|
||||
if parent.has_node("%" + prop):
|
||||
# 注入属性
|
||||
var node = parent.get_node_or_null("%" + prop)
|
||||
if node:
|
||||
parent[data['name']] = node
|
||||
else:
|
||||
if open_err:
|
||||
printerr("没有 ", prop, " 属性相关节点")
|
||||
|
||||
if parent.is_inside_tree():
|
||||
method.call()
|
||||
else:
|
||||
parent.tree_entered.connect(method, Object.CONNECT_ONE_SHOT)
|
||||
return true
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
#============================================================
|
||||
# Table Data Util
|
||||
#============================================================
|
||||
# - author: zhangxuetu
|
||||
# - datetime: 2023-05-17 19:14:50
|
||||
# - version: 4.0
|
||||
#============================================================
|
||||
class_name TableDataUtil
|
||||
|
||||
|
||||
class SingletonData:
|
||||
|
||||
static func get_value(key, default: Callable):
|
||||
if not Engine.has_meta(key):
|
||||
Engine.set_meta(key, default.call())
|
||||
return Engine.get_meta(key)
|
||||
|
||||
static func get_child_value(key, child_key, child_default: Callable):
|
||||
var data = get_value(key, func(): return {})
|
||||
if data.has(child_key):
|
||||
return data[child_key]
|
||||
else:
|
||||
data[child_key] = child_default.call()
|
||||
return data[child_key]
|
||||
|
||||
|
||||
|
||||
class Files:
|
||||
|
||||
static func load_file(path: String):
|
||||
if FileAccess.file_exists(path):
|
||||
var bytes = FileAccess.get_file_as_bytes(path)
|
||||
return bytes_to_var(bytes)
|
||||
|
||||
static func make_dir(dir: String) -> bool:
|
||||
if not DirAccess.dir_exists_absolute(dir):
|
||||
DirAccess.make_dir_recursive_absolute(dir)
|
||||
return true
|
||||
return false
|
||||
|
||||
static func save_data(path: String, data) -> bool:
|
||||
make_dir(path.get_base_dir())
|
||||
if not path.is_empty():
|
||||
var bytes : PackedByteArray = var_to_bytes(data)
|
||||
var writer : FileAccess = FileAccess.open(path, FileAccess.WRITE)
|
||||
if writer.get_open_error() != OK:
|
||||
printerr("打开文件失败!", writer.get_open_error())
|
||||
return false
|
||||
|
||||
writer.store_buffer(bytes)
|
||||
if writer.get_error() != OK:
|
||||
printerr("写入文件失败:", writer.get_error())
|
||||
return false
|
||||
|
||||
writer = null
|
||||
return true
|
||||
return false
|
||||
|
||||
static func save_as_string(path:String, data):
|
||||
make_dir(path.get_base_dir())
|
||||
var writer = FileAccess.open(path, FileAccess.WRITE)
|
||||
writer.store_string(
|
||||
JSON.stringify(data)
|
||||
if not data is String
|
||||
else data
|
||||
)
|
||||
|
||||
static func read_as_string(path: String) -> String:
|
||||
if FileAccess.file_exists(path):
|
||||
var reader = FileAccess.open(path, FileAccess.READ)
|
||||
return reader.get_as_text()
|
||||
return ""
|
||||
|
||||
static func read_csv_file(path: String, delim: String = ",") -> Array[PackedStringArray]:
|
||||
if FileAccess.file_exists(path):
|
||||
var reader = FileAccess.open(path, FileAccess.READ)
|
||||
var lines : Array[PackedStringArray]= []
|
||||
var line = reader.get_csv_line(delim)
|
||||
while line != PackedStringArray([""]):
|
||||
lines.append(line)
|
||||
line = reader.get_csv_line(delim)
|
||||
return lines
|
||||
return []
|
||||
|
||||
static func get_absolute_path(path: String) -> String:
|
||||
var reader = FileAccess.open(path, FileAccess.READ)
|
||||
if reader:
|
||||
return reader.get_path_absolute()
|
||||
return ""
|
||||
|
||||
|
||||
|
||||
class Classes:
|
||||
|
||||
static func get_propertys(script: Script) -> Array[String]:
|
||||
return SingletonData.get_child_value("TableDataUtil_Classes_propertys", script, func():
|
||||
var list : Array[String] = []
|
||||
list.append_array(script \
|
||||
.get_script_property_list() \
|
||||
.map(func(data): return data['name'])
|
||||
.filter(func(name: String): return name.find(".") == -1)
|
||||
)
|
||||
return list
|
||||
)
|
||||
|
||||
static func set_property_by_dict(object: Object, dict: Dictionary):
|
||||
for property in dict:
|
||||
if property in object:
|
||||
object[property] = dict[property]
|
||||
|
||||
static func get_dict_by_property(object: Object) -> Dictionary:
|
||||
var dict : Dictionary = {}
|
||||
var list = get_propertys(object.get_script())
|
||||
for property in get_propertys(object.get_script()):
|
||||
dict[property] = object[property]
|
||||
return dict
|
||||
|
||||
|
||||
|
||||
class Editor:
|
||||
|
||||
## 获取编辑器接口
|
||||
static func get_editor_interface() -> EditorInterface:
|
||||
if not Engine.is_editor_hint():
|
||||
return null
|
||||
const KEY = "TableDataUtil_get_editor_interface"
|
||||
if Engine.has_meta(KEY):
|
||||
return Engine.get_meta(KEY)
|
||||
else:
|
||||
var plugin = ClassDB.instantiate("EditorPlugin")
|
||||
Engine.set_meta(KEY, plugin.get_editor_interface())
|
||||
return plugin.get_editor_interface()
|
||||
|
||||
## 是否开启了插件
|
||||
static func is_enabled() -> bool:
|
||||
if not Engine.is_editor_hint():
|
||||
return false
|
||||
if get_editor_interface() == null:
|
||||
return false
|
||||
return get_editor_interface().is_plugin_enabled("table_data_editor")
|
||||
|
||||
## 当前插件是否是打开的
|
||||
static func is_main_node() -> bool:
|
||||
var node = get_editor_interface().get_editor_main_screen()
|
||||
for child in node.get_children():
|
||||
if child is Control and child.visible:
|
||||
# 显示的是当前插件的名称
|
||||
return child.name == 'table_data_editor'
|
||||
return false
|
||||
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user