filtering

This commit is contained in:
teebarjunk 2021-10-11 12:41:26 -04:00
parent 3e482dd5e0
commit 6c09d1d66d
27 changed files with 973 additions and 606 deletions

4
.gitignore vendored
View File

@ -12,5 +12,5 @@ data_*/
# Text editor related
test_files/
.trash/
.trash_info.json
.te_trash/
.te_trash_info.json

3
Node2D.tscn Normal file
View File

@ -0,0 +1,3 @@
[gd_scene format=2]
[node name="Node2D" type="Node2D"]

63
README.md Normal file
View File

@ -0,0 +1,63 @@
# Text Editor
Version 1.0
***Warning: Use at your own risk. Backup your files before testing.***
# Features
- Tabs with scroll
- File filtering
- Highlighting for common file formats (`md` `json`...)
- Tag filtering system
- File Management:
- Creation
- Renaming
- Recycling
- Many little *Ease of life* functions:
- Folder open/close
- Comment toggling for:
- `.md`: `<!-- -->`
- `.json`: `/* */`
- `.ini`: `; `
- `.cfg`: `; `
- `.yaml`: `# `
- Table of Contents (Symbols)
# Controls
- `ctrl + W` Close file
- `ctrl + shift + W` Open last closed file
- `ctrl + tab` Select next open file
- `ctrl + shift + tab` Select last open file
- `ctrl + mouse wheel` Adjust font size
- `ctrl + up` `ctrl + down` Move selected lines
- `ctrl + /` Toggle line comments
- `ctrl + M` Toggle file meta info
# Symbols and Tags
To make it easier to find stuff there is a *Symbol* viewer.
- `Markdown` uses headings `# Heading`
- `JSON` uses Dictionaries `"object": {`
- `YAML` uses Dictionaries `object: `
- `ini` `cfg` use headings `[heading]`
Symbols can have tags. Tags are added with comments.
- `Markdown` uses `<!-- #tag1 #tag2 -->`
- `JSON` uses `/* #tag1 #tag2 */` or `"#": "#tag1 #tag2"`
- `YAML` uses `# #tag1 #tag2` or `"#": "#tag1 #tag2"`
- `ini` `cfg` uses `; #tag1 #tag2`
Symbols are per file, tags are shared across files.
When a file is opened with tags, they show up in bottom right *Tag Container*.
Click them to toggle on and off.\
This will then highlight *Files* and *Symbols* that have that tag.
# Todo
- [ ] Search
- [ ] Find and replace
- [ ] Meta data based on format.

View File

@ -1,255 +0,0 @@
[gd_scene load_steps=10 format=2]
[ext_resource path="res://addons/text_editor/file_buttons.gd" type="Script" id=1]
[ext_resource path="res://addons/text_editor/TextEditor.gd" type="Script" id=2]
[ext_resource path="res://addons/text_editor/list_files.gd" type="Script" id=3]
[ext_resource path="res://addons/text_editor/FileEditorTab.gd" type="Script" id=4]
[ext_resource path="res://addons/text_editor/list_symbols.gd" type="Script" id=5]
[ext_resource path="res://addons/text_editor/tab_scroll.gd" type="Script" id=6]
[ext_resource path="res://addons/text_editor/list_tags.gd" type="Script" id=7]
[ext_resource path="res://addons/text_editor/line_edit.gd" type="Script" id=8]
[ext_resource path="res://addons/text_editor/meta_panel.gd" type="Script" id=9]
[node name="text_editor" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/separation = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c" type="PanelContainer" parent="c"]
margin_right = 1024.0
margin_bottom = 34.0
[node name="c" type="HBoxContainer" parent="c/c"]
margin_left = 7.0
margin_top = 7.0
margin_right = 1017.0
margin_bottom = 27.0
script = ExtResource( 1 )
[node name="test" type="Button" parent="c/c/c"]
margin_right = 56.0
margin_bottom = 20.0
text = "update"
[node name="file_button" type="MenuButton" parent="c/c/c"]
margin_left = 60.0
margin_right = 92.0
margin_bottom = 20.0
text = "file"
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c3" type="HSplitContainer" parent="c"]
margin_top = 34.0
margin_right = 1024.0
margin_bottom = 600.0
size_flags_vertical = 3
split_offset = -300
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c2" type="PanelContainer" parent="c/c3"]
margin_right = 206.0
margin_bottom = 566.0
rect_min_size = Vector2( 200, 0 )
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="c" type="Panel" parent="c/c3/c2"]
margin_left = 7.0
margin_top = 7.0
margin_right = 199.0
margin_bottom = 559.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="list_files" type="RichTextLabel" parent="c/c3/c2/c"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
bbcode_enabled = true
meta_underlined = false
script = ExtResource( 3 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="popup" type="PopupMenu" parent="c/c3/c2/c/list_files"]
margin_right = 69.0
margin_bottom = 68.0
[node name="drag_label" type="RichTextLabel" parent="c/c3/c2/c/list_files"]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
mouse_filter = 2
bbcode_enabled = true
fit_content_height = true
[node name="c" type="HSplitContainer" parent="c/c3"]
margin_left = 218.0
margin_right = 1024.0
margin_bottom = 566.0
size_flags_horizontal = 3
size_flags_vertical = 3
split_offset = -80
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c" type="VBoxContainer" parent="c/c3/c"]
margin_right = 614.0
margin_bottom = 566.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="line_edit" type="LineEdit" parent="c/c3/c/c"]
visible = false
margin_right = 614.0
margin_bottom = 24.0
script = ExtResource( 8 )
[node name="tab_container" type="TabContainer" parent="c/c3/c/c"]
margin_right = 614.0
margin_bottom = 547.0
size_flags_horizontal = 3
size_flags_vertical = 3
tab_align = 0
drag_to_rearrange_enabled = true
script = ExtResource( 6 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="tab_prefab" type="TextEdit" parent="c/c3/c/c/tab_container"]
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 4.0
margin_top = 32.0
margin_right = -4.0
margin_bottom = -4.0
size_flags_horizontal = 3
size_flags_vertical = 3
highlight_current_line = true
syntax_highlighting = true
show_line_numbers = true
draw_tabs = true
breakpoint_gutter = true
fold_gutter = true
highlight_all_occurrences = true
minimap_draw = true
script = ExtResource( 4 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="meta" type="RichTextLabel" parent="c/c3/c/c"]
margin_top = 551.0
margin_right = 614.0
margin_bottom = 566.0
bbcode_enabled = true
fit_content_height = true
script = ExtResource( 9 )
[node name="c2" type="PanelContainer" parent="c/c3/c"]
margin_left = 626.0
margin_right = 806.0
margin_bottom = 566.0
rect_min_size = Vector2( 100, 0 )
size_flags_vertical = 3
[node name="c" type="VSplitContainer" parent="c/c3/c/c2"]
margin_left = 7.0
margin_top = 7.0
margin_right = 173.0
margin_bottom = 559.0
custom_constants/autohide = 0
[node name="c" type="Panel" parent="c/c3/c/c2/c"]
margin_right = 166.0
margin_bottom = 270.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="list_symbols" type="RichTextLabel" parent="c/c3/c/c2/c/c"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_vertical = 3
bbcode_enabled = true
bbcode_text = "tags"
meta_underlined = false
text = "tags"
script = ExtResource( 5 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c2" type="Panel" parent="c/c3/c/c2/c"]
margin_top = 282.0
margin_right = 166.0
margin_bottom = 552.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="list_tags" type="RichTextLabel" parent="c/c3/c/c2/c/c2"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
bbcode_enabled = true
bbcode_text = "tags"
meta_underlined = false
text = "tags"
script = ExtResource( 7 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="popup" type="ConfirmationDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -100.0
margin_top = -35.0
margin_right = 100.0
margin_bottom = 35.0
[node name="popup_unsaved" type="ConfirmationDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -100.0
margin_top = -35.0
margin_right = 100.0
margin_bottom = 35.0
window_title = "Warning"
dialog_text = "Unsaved data will be lost."
[node name="file_dialog" type="FileDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -297.5
margin_top = -157.0
margin_right = 297.5
margin_bottom = 157.0
__meta__ = {
"_edit_use_anchors_": false
}

View File

@ -0,0 +1,34 @@
extends TabContainer
onready var editor:TextEditor = owner
var mouse:bool = false
func _ready():
var _e
_e = connect("mouse_entered", self, "set", ["mouse", true])
_e = connect("mouse_exited", self, "set", ["mouse", false])
func _input(e):
if not editor.is_plugin_active():
return
if mouse and e is InputEventMouseButton and e.pressed:
if e.button_index == BUTTON_WHEEL_DOWN:
prev()
get_tree().set_input_as_handled()
elif e.button_index == BUTTON_WHEEL_UP:
next()
get_tree().set_input_as_handled()
if e is InputEventKey and e.pressed and e.control and e.scancode == KEY_TAB:
if e.shift:
prev()
get_tree().set_input_as_handled()
else:
next()
get_tree().set_input_as_handled()
func prev(): current_tab = wrapi(current_tab - 1, 0, get_child_count())
func next(): current_tab = wrapi(current_tab + 1, 0, get_child_count())

View File

@ -1,3 +1,4 @@
tool
extends TE_RichTextLabel
onready var editor:TextEditor = owner

View File

@ -0,0 +1,18 @@
tool
extends RichTextLabel
var editor:TextEditor
func _ready():
add_font_override("normal_font", editor.FONT_R)
add_font_override("bold_font", editor.FONT_B)
add_font_override("italics_font", editor.FONT_I)
add_font_override("bold_italics_font", editor.FONT_BI)
func _process(_delta):
set_global_position(get_global_mouse_position())
func _input(e):
if e is InputEventMouseButton:
if (e.button_index == BUTTON_LEFT and not e.pressed) or (e.button_index == BUTTON_RIGHT and e.pressed):
queue_free()

View File

@ -1,7 +1,7 @@
tool
extends TextEdit
onready var tabs:TabContainer = get_parent()
onready var editor:TextEditor = get_parent().owner
var editor:TextEditor
var helper:TE_ExtensionHelper
var temporary:bool = false setget set_temporary
@ -17,6 +17,8 @@ var last_selection:Array = [0, 0, 0, 0]
func _ready():
var _e
if not editor:
editor = owner
_e = editor.connect("save_files", self, "save_file")
_e = editor.connect("file_selected", self, "_file_selected")
_e = editor.connect("file_renamed", self, "_file_renamed")
@ -30,9 +32,13 @@ func _file_renamed(old_path:String, new_path:String):
update_name()
func _input(e):
if not editor.is_plugin_active():
return
if not visible:
return
# remember last selection
if e is InputEventKey and e.pressed:
last_key = e.scancode
last_shift = e.shift
@ -45,6 +51,7 @@ func _input(e):
else:
last_selected = false
# move lines up/down
if e is InputEventKey and e.control and e.shift and e.pressed:
var f
var t
@ -84,7 +91,9 @@ func _unhandled_key_input(e):
func _file_selected(p:String):
if p and p == file_path:
grab_focus()
grab_click_focus()
update_symbols()
update_heading()
func text_changed():
if last_selected:
@ -182,7 +191,19 @@ func update_name():
if temporary: n = "?" + n
if modified: n = "*" + n
tabs.set_tab_title(get_index(), n)
editor.tab_parent.set_tab_title(get_index(), n)
update_heading()
func update_heading():
# set window "file (directory)"
var f = file_path.get_file()
if modified:
f = "*" + f
var d = file_path.get_base_dir().get_file()
if d:
OS.set_window_title("%s (%s)" % [f, d])
else:
OS.set_window_title(f)
func needs_save() -> bool:
return modified or not File.new().file_exists(file_path)

View File

@ -0,0 +1,238 @@
tool
extends RichTextLabel
onready var editor:TextEditor = owner
onready var file_popup:PopupMenu = $file_popup
onready var dir_popup:PopupMenu = $dir_popup
const DragLabel = preload("res://addons/text_editor/TE_DragLabel.gd")
var drag_label:RichTextLabel
var files:Array = []
var dirs:Array = []
var selected
var hovered:String = ""
var dragging:String = ""
var drag_start:Vector2
func _ready():
var _e
_e = editor.connect("updated_file_list", self, "_redraw")
_e = editor.connect("tags_updated", self, "_redraw")
_e = editor.connect("file_opened", self, "_file_opened")
_e = editor.connect("file_closed", self, "_file_closed")
_e = editor.connect("file_selected", self, "_file_selected")
_e = editor.connect("file_renamed", self, "_file_renamed")
_e = connect("meta_hover_started", self, "_meta_entered")
_e = connect("meta_hover_ended", self, "_meta_exited")
# hint
theme = Theme.new()
theme.set_font("font", "TooltipLabel", editor.FONT_R)
# file popup
file_popup.clear()
file_popup.rect_size = Vector2.ZERO
file_popup.add_item("Rename")
file_popup.add_separator()
file_popup.add_item("Remove")
_e = file_popup.connect("index_pressed", self, "_file_popup")
file_popup.add_font_override("font", TextEditor.FONT)
# dir popup
dir_popup.clear()
dir_popup.rect_size = Vector2.ZERO
dir_popup.add_item("Create new file")
_e = dir_popup.connect("index_pressed", self, "_dir_popup")
dir_popup.add_font_override("font", TextEditor.FONT)
add_font_override("normal_font", editor.FONT_R)
add_font_override("bold_font", editor.FONT_B)
add_font_override("italics_font", editor.FONT_I)
add_font_override("bold_italics_font", editor.FONT_BI)
func _dir_popup(index:int):
var p = _meta_to_file(selected)
var type = p[0]
var file = p[1]
match dir_popup.get_item_text(index):
"Create new file":
editor.popup_create_file(file)
func _file_popup(index:int):
var p = _meta_to_file(selected)
var type = p[0]
var file = p[1]
match file_popup.get_item_text(index):
"Rename":
var fname:String = file.get_file()
var i:int = fname.find(".")
editor.line_edit.display(fname, self, "_renamed")
editor.line_edit.select(0, i)
"Remove":
if type == "f":
editor.recycle_file(file)
_:
selected = {}
func _renamed(new_file:String):
var p = _meta_to_file(selected)
var type = p[0]
var file = p[1]
var old_path:String = file
var old_file:String = old_path.get_file()
if new_file != old_file:
var new_path:String = old_path.get_base_dir().plus_file(new_file)
editor.rename_file(old_path, new_path)
selected = {}
func _input(e:InputEvent):
if not editor.is_plugin_active():
return
if e is InputEventMouseButton and hovered:
var p = _meta_to_file(hovered)
var type = p[0]
var file = p[1]
if e.button_index == BUTTON_LEFT:
if e.pressed:
dragging = hovered
if type == "f":
drag_label = DragLabel.new()
drag_label.editor = editor
drag_label.set_bbcode(file.get_file())
editor.add_child(drag_label)
else:
if dragging and dragging != hovered:
var p2 = _meta_to_file(dragging)
var drag_type = p[0]
var drag_file = p[1]
if drag_type == "f" and type == "d":
var dir:String = file
var old_path:String = drag_file
var new_path:String = dir.plus_file(old_path.get_file())
editor.rename_file(old_path, new_path)
else:
match type:
# toggle directory
"d":
p[2].open = not p[2].open
_redraw()
# select
"f":
editor.select_file(file)
get_tree().set_input_as_handled()
elif e.button_index == BUTTON_RIGHT:
if e.pressed:
selected = hovered
match type:
"d":
dir_popup.set_global_position(get_global_mouse_position())
dir_popup.popup()
"f":
file_popup.set_global_position(get_global_mouse_position())
file_popup.popup()
get_tree().set_input_as_handled()
func _meta_to_file(m:String):
var p = m.split(":", true, 1)
var type = p[0]
var index = int(p[1])
match type:
"d":
return [type, dirs[index].file_path, dirs[index]]
"f":
return [type, files[index]]
func _meta_entered(m):
hovered = m
var f = _meta_to_file(m)
match f[0]:
"f", "d": hint_tooltip = f[1]
func _meta_exited(_m):
hovered = ""
hint_tooltip = ""
func _file_opened(_file_path:String): _redraw()
func _file_closed(_file_path:String): _redraw()
func _file_selected(_file_path:String): _redraw()
func _file_renamed(_op:String, _np:String): _redraw()
var lines:PoolStringArray = PoolStringArray()
func _redraw():
lines = PoolStringArray()
dirs.clear()
files.clear()
_draw_dir(editor.file_list[""], 0)
set_bbcode(lines.join("\n"))
func clr(s:String, c:Color) -> String: return "[color=#%s]%s[/color]" % [c.to_html(), s]
func i(s:String) -> String: return "[i]%s[/i]" % s
func b(s:String) -> String: return "[b]%s[/b]" % s
func url(s:String, url:String) -> String: return "[url=%s]%s[/url]" % [url, s]
const FOLDER:String = "🗀" # not visible in godot
func _draw_dir(dir:Dictionary, deep:int):
var space = clr("".repeat(deep), Color.white.darkened(.8))
var file:String = dir.file_path
var name:String = b(file.get_file())
var head:String = "" if dir.open else ""
var dir_index:int = len(dirs)
var link:String = url(space+FOLDER+head+" "+name, "d:%s" % dir_index)
lines.append(clr(link, Color.white.darkened(.7)))
dirs.append(dir)
var sel = editor.get_selected_tab()
sel = sel.file_path if sel else ""
if dir.open:
var i = 0
var last = len(dir.all)-1
for path in dir.all:
var file_path = dir.all[path]
# dir
if file_path is Dictionary:
_draw_dir(file_path, deep+1)
# file
else:
file = path.get_file()
var is_selected = file_path == sel
head = "┣╸" if i != last else "┗╸"
if is_selected:
head = clr(head, Color.white.darkened(.5))
else:
head = clr(head, Color.white.darkened(.8))
var p = file.split(".", true, 1)
file = p[0]
var color = Color.white if editor.is_tagged(file_path) else Color.white.darkened(.5)
if editor.is_selected(file_path):
file = clr(file, color)
elif editor.is_opened(file_path):
file = clr(file, color.darkened(.5))
else:
file = i(clr(file, color.darkened(.75)))
var ext = clr("." + p[1], Color.white.darkened(.75))
var line = space + head + file + ext
lines.append(url(line, "f:%s" % len(files)))
files.append(file_path)
i += 1

View File

@ -1,3 +1,4 @@
tool
extends RichTextLabel
onready var editor:TextEditor = owner
@ -13,6 +14,8 @@ func _ready():
add_font_override("bold_font", editor.FONT_B)
add_font_override("italics_font", editor.FONT_I)
add_font_override("bold_italics_font", editor.FONT_BI)
call_deferred("_redraw")
func _hovered(_id):
pass

View File

@ -1,3 +1,4 @@
tool
extends RichTextLabel
onready var editor:TextEditor = owner
@ -15,6 +16,8 @@ func _ready():
add_font_override("bold_font", editor.FONT_B)
add_font_override("italics_font", editor.FONT_I)
add_font_override("bold_italics_font", editor.FONT_BI)
call_deferred("_redraw")
func _hovered(_id):
pass
@ -64,4 +67,4 @@ func _redraw():
t.append(x)
tag_indices.append(tag)
set_bbcode(t.join(" "))
set_bbcode("[center]" + t.join(" "))

View File

@ -1,4 +1,5 @@
extends Node
tool
extends Control
class_name TextEditor
const FONT:DynamicFont = preload("res://addons/text_editor/fonts/font.tres")
@ -8,9 +9,12 @@ const FONT_B:DynamicFont = preload("res://addons/text_editor/fonts/font_b.tres")
const FONT_I:DynamicFont = preload("res://addons/text_editor/fonts/font_i.tres")
const FONT_BI:DynamicFont = preload("res://addons/text_editor/fonts/font_bi.tres")
const EXTENSIONS:PoolStringArray = PoolStringArray([
const MAIN_EXTENSIONS:PoolStringArray = PoolStringArray([
"txt", "md", "json", "csv", "cfg", "ini", "yaml"
])
const INTERNAL_EXTENSIONS:PoolStringArray = PoolStringArray([
"gd", "import", "gdignore", "gitignore"
])
const FILE_FILTERS:PoolStringArray = PoolStringArray([
"*.txt ; Text",
"*.md ; Markdown",
@ -18,25 +22,39 @@ const FILE_FILTERS:PoolStringArray = PoolStringArray([
"*.csv ; Comma Seperated Values",
"*.cfg ; Config",
"*.ini ; Config",
"*.yaml ; YAML"
"*.yaml ; YAML",
])
var plugin = null
var plugin_hint:bool = false
# hide dirs
var show_dir_empty:bool = true
var show_dir_hidden:bool = true
var show_dir_gdignore:bool = true
#
var show_dir_git:bool = false
var show_dir_import:bool = false
var show_dir_trash:bool = false
# hide files
var show_file_hidden:bool = true
var color_text:Color = Color.white
var color_comment:Color = Color.darkolivegreen
var color_symbol:Color = Color.white.darkened(.5)
var color_comment:Color = Color.white.darkened(.6)
var color_symbol:Color = Color.deepskyblue
var color_var:Color = Color.orange
var color_varname:Color = Color.white.darkened(.25)
onready var test_button:Node = $c/c/c/test
onready var tab_parent:TabContainer = $c/c3/c/c/tab_container
onready var tab_prefab:Node = $c/c3/c/c/tab_container/tab_prefab
onready var tab_prefab:Node = $file_editor
onready var popup:ConfirmationDialog = $popup
onready var popup_unsaved:ConfirmationDialog = $popup_unsaved
onready var file_dialog:FileDialog = $file_dialog
onready var line_edit:LineEdit = $c/c3/c/c/line_edit
onready var menu_file:MenuButton = $c/c/c/file_button
var ext_menu:PopupMenu = PopupMenu.new()
var hide_menu:PopupMenu = PopupMenu.new()
signal updated_file_list()
signal file_opened(file_path)
@ -44,15 +62,14 @@ signal file_closed(file_path)
signal file_selected(file_path)
signal file_saved(file_path)
signal file_renamed(old_path, new_path)
#signal file_symbols_updated(file_path)
signal symbols_updated()
signal tags_updated()
signal save_files()
var current_directory:String = ""
var current_directory:String = "res://"
var dirs:Array = []
var file_list:Dictionary = {}
var ext_counts:Dictionary = {}
#var ext_counts:Dictionary = {}
var symbols:Dictionary = {}
var tags:Array = []
var tags_enabled:Dictionary = {}
@ -63,11 +80,15 @@ var opened:Array = []
var closed:Array = []
func _ready():
if not is_plugin_active():
return
# not needed when editor plugin
# get_tree().set_auto_accept_quit(false)
var _e
_e = test_button.connect("pressed", self, "_debug_pressed")
test_button.add_font_override("font", FONT_R)
# popup unsaved
popup_unsaved.get_ok().text = "Ok"
@ -78,21 +99,55 @@ func _ready():
TE_Util.dig(popup_unsaved, self, "_apply_fonts")
# menu
var p = menu_file.get_popup()
menu_file.add_font_override("font", FONT_R)
var p:PopupMenu = menu_file.get_popup()
p.clear()
p.add_font_override("font", FONT_R)
p.add_item("New File")
_e = p.connect("index_pressed", self, "_menu_file")
# extensions
ext_menu.set_name("Extensions")
# hide
hide_menu.clear()
hide_menu.set_name("Directories")
hide_menu.add_font_override("font", FONT_R)
hide_menu.add_check_item("Hidden", 0)
hide_menu.add_check_item("Empty", 1)
hide_menu.add_check_item(".gdignore", 2)
hide_menu.set_item_checked(0, show_dir_hidden)
hide_menu.set_item_checked(1, show_dir_gdignore)
hide_menu.set_item_checked(2, show_dir_empty)
hide_menu.add_separator()
hide_menu.add_check_item(".ignore/", 4)
hide_menu.add_check_item(".git/", 5)
hide_menu.add_check_item(".trash/", 6)
p.add_child(hide_menu)
p.add_submenu_item("Directories", "Directories")
_e = hide_menu.connect("index_pressed", self, "_menu_hide")
# hide extensions
ext_menu.clear()
ext_menu.set_name("Files")
ext_menu.add_font_override("font", FONT_R)
for i in len(EXTENSIONS):
var ext = EXTENSIONS[i]
ext_menu.add_check_item(ext, i)
ext_menu.set_item_checked(i, true)
ext_menu.add_check_item("Hidden", 0)
ext_menu.set_item_checked(0, show_file_hidden)
ext_menu.add_separator()
for i in len(MAIN_EXTENSIONS):
var ext = MAIN_EXTENSIONS[i]
ext_menu.add_check_item("*." + ext, i+2)
ext_menu.set_item_checked(i+2, true)
exts_enabled.append(ext)
ext_menu.add_separator()
for i in len(INTERNAL_EXTENSIONS):
var ext = INTERNAL_EXTENSIONS[i]
var id = i+len(MAIN_EXTENSIONS)+3
ext_menu.add_check_item("*." + ext, id)
ext_menu.set_item_checked(id, false)
p.add_child(ext_menu)
p.add_submenu_item("Extensions", "Extensions")
p.add_submenu_item("Files", "Files")
_e = ext_menu.connect("index_pressed", self, "_menu_extension")
# file dialog
@ -102,7 +157,6 @@ func _ready():
# tab control
_e = tab_parent.connect("tab_changed", self, "_tab_changed")
tab_parent.remove_child(tab_prefab)
#
tab_parent.add_font_override("font", FONT_R)
@ -119,7 +173,16 @@ func _ready():
# return
# get_tree().quit()
func is_plugin_active():
if not Engine.editor_hint:
return true
return plugin_hint and visible
func _input(e):
if not is_plugin_active():
return
if e is InputEventMouseButton and e.control:
if e.button_index == BUTTON_WHEEL_DOWN:
FONT.size = int(max(8, FONT.size - 1))
@ -138,16 +201,58 @@ func _menu_file(a):
match menu_file.get_popup().items[a]:
"New File": popup_create_file()
func _menu_extension(index:int):
var ext = EXTENSIONS[index]
var toggled = ext in exts_enabled
if toggled:
exts_enabled.erase(ext)
elif not ext in exts_enabled:
exts_enabled.append(ext)
ext_menu.set_item_checked(index, not toggled)
func _menu_hide(index:int):
match index:
0:
show_dir_hidden = not show_dir_hidden
hide_menu.set_item_checked(index, show_dir_hidden)
1:
show_dir_empty = not show_dir_empty
hide_menu.set_item_checked(index, show_dir_empty)
2:
show_dir_gdignore = not show_dir_gdignore
hide_menu.set_item_checked(index, show_dir_gdignore)
4:
show_dir_import = not show_dir_import
hide_menu.set_item_checked(index, show_dir_import)
4:
show_dir_git = not show_dir_git
hide_menu.set_item_checked(index, show_dir_git)
5:
show_dir_trash = not show_dir_trash
hide_menu.set_item_checked(index, show_dir_trash)
refresh_files()
func _menu_extension(index:int):
# hidden files
if index == 0:
show_file_hidden = not show_file_hidden
ext_menu.set_item_checked(index, show_file_hidden)
# main extensions
elif index-2 < len(MAIN_EXTENSIONS):
var ext = MAIN_EXTENSIONS[index-2]
var toggled = ext in exts_enabled
if toggled:
exts_enabled.erase(ext)
elif not ext in exts_enabled:
exts_enabled.append(ext)
ext_menu.set_item_checked(index, not toggled)
refresh_files()
# internal extensions
elif index-3-len(MAIN_EXTENSIONS) < len(INTERNAL_EXTENSIONS):
var ext = INTERNAL_EXTENSIONS[index-3-len(MAIN_EXTENSIONS)]
var toggled = ext in exts_enabled
if toggled:
exts_enabled.erase(ext)
elif not ext in exts_enabled:
exts_enabled.append(ext)
ext_menu.set_item_checked(index, not toggled)
refresh_files()
func _file_dialog_file(file_path:String):
match file_dialog.get_meta("mode"):
"create": create_file(file_path)
@ -229,10 +334,8 @@ func _unhandled_key_input(e:InputEventKey):
# close/unclose tab
elif e.scancode == KEY_W:
if e.shift:
print("open last tab")
open_last_file()
else:
print("close tab ")
close_selected()
elif e.scancode == KEY_R:
@ -244,11 +347,11 @@ func _unhandled_key_input(e:InputEventKey):
get_tree().set_input_as_handled()
func sort_files():
TE_Util.dig(file_list, self, "_sort_files")
TE_Util.dig(file_list, self, "_sort")
emit_signal("updated_file_list")
func _sort_files(d:Dictionary):
return TE_Util.sort_on_ext(d)
func _sort(dir:Dictionary):
return TE_Util.sort_on_ext(dir)
func get_selected_file() -> String:
var node = get_selected_tab()
@ -280,13 +383,20 @@ func save_file(file_path:String, text:String):
emit_signal("file_saved", file_path)
func open_last_file():
print("open last closed")
print(closed)
print(opened)
if closed:
closed.pop_back()
var file_path = closed.pop_back()
open_file(file_path)
select_file(file_path)
func close_selected():
var file = get_selected_file()
if file:
close_file(file)
var tab = get_selected_tab()
if tab:
tab.close()
else:
print("cant close")
func close_file(file_path:String):
var tab = get_tab(file_path)
@ -295,15 +405,12 @@ func close_file(file_path:String):
func _close_file(file_path, remember:bool=true):
if remember:
closed.append(opened.pop_back())
closed.append(file_path)
var tab = get_tab(file_path)
tab_parent.remove_child(tab)
tab.queue_free()
emit_signal("file_closed", file_path)
if opened:
select_file(opened[-1])
func open_file(file_path:String, temporary:bool=false):
var tab = get_tab(file_path)
@ -312,6 +419,8 @@ func open_file(file_path:String, temporary:bool=false):
else:
tab = tab_prefab.duplicate()
tab.visible = true
tab.editor = self
tab_parent.add_child(tab)
tab.set_owner(self)
tab.load_file(file_path)
@ -360,7 +469,6 @@ func recycle_file(file_path:String):
if opened:
select_file(opened[-1])
func rename_file(old_path:String, new_path:String):
if old_path == new_path or not old_path or not new_path:
return
@ -394,10 +502,9 @@ func select_file(file_path:String):
tab_parent.current_tab = get_tab(file_path).get_index()
_selected_file_changed(file_path)
func set_directory(path:String="res://test_files"):
func set_directory(path:String=current_directory):
var gpath = ProjectSettings.globalize_path(path)
var dname = gpath.get_file()
OS.set_window_title("%s (%s)" % [dname, gpath])
current_directory = path
file_dialog.current_dir = path
refresh_files()
@ -422,7 +529,7 @@ func get_all_tabs() -> Array:
return tab_parent.get_children()
func refresh_files():
ext_counts.clear()
# ext_counts.clear()
dirs.clear()
file_list.clear()
var dir = Directory.new()
@ -433,39 +540,67 @@ func refresh_files():
sort_files()
func _scan_dir(id:String, path:String, dir:Directory, list:Dictionary):
func show_dir(fname:String) -> bool:
if not show_dir_gdignore and File.new().file_exists(fname.plus_file(".gdignore")):
return false
if fname.begins_with("."):
if not show_dir_hidden: return false
if not show_dir_import and fname == ".import": return false
if not show_dir_git and fname == ".git": return false
if not show_dir_trash and fname == ".trash": return false
return true
func show_file(fname:String) -> bool:
if fname.begins_with("."):
if not show_file_hidden: return false
var ext = get_extension(fname)
return ext in exts_enabled
func _scan_dir(id:String, path:String, dir:Directory, last_dir:Dictionary):
var _e = dir.list_dir_begin(true, false)
dirs.append(path)
var files = {}
list[id] = { file_path=path, files=files, open=true }
var a_dirs_and_files = {}
var a_files = []
var a_dirs = []
var info = { file_path=path, all=a_dirs_and_files, files=a_files, dirs=a_dirs, open=true }
var fname = dir.get_next()
while fname:
if not fname.begins_with("."):
var file_path = dir.get_current_dir().plus_file(fname)
if dir.current_is_dir():
# ignore folders with a .gdignore file.
if not fname == ".import" and not File.new().file_exists(file_path.plus_file(".gdignore")):
var sub_dir = Directory.new()
sub_dir.open(file_path)
_scan_dir(fname, file_path, sub_dir, files)
else:
# ignore .import files
if not file_path.ends_with(".import"):
var ext = get_extension(file_path)
if ext in exts_enabled:
files[fname] = file_path
if not ext in ext_counts:
ext_counts[ext] = 1
else:
ext_counts[ext] += 1
var file_path = dir.get_current_dir().plus_file(fname)
if dir.current_is_dir():
if show_dir(fname):
var sub_dir = Directory.new()
sub_dir.open(file_path)
_scan_dir(fname, file_path, sub_dir, a_dirs_and_files)
else:
if show_file(fname):
a_dirs_and_files[fname] = file_path
fname = dir.get_next()
dir.list_dir_end()
# is empty? ignore
if id and not (show_dir_empty or a_dirs_and_files):
return
# add to last
last_dir[id] = info
if path == current_directory:
print(JSON.print(info, "\t"))
for p in a_dirs_and_files:
if a_dirs_and_files[p] is Dictionary:
a_dirs.append(p)
else:
a_files.append(p)
static func get_extension(file_path:String) -> String:
var file = file_path.get_file()

View File

@ -63,6 +63,14 @@ static func _dig_node(d:Node, f:FuncRef):
for i in d.get_child_count():
_dig_node(d.get_child(i), f)
static func file_size(path:String) -> String:
var f:File = File.new()
if f.open(path, File.READ) == OK:
var bytes = f.get_len()
f.close()
return String.humanize_size(bytes)
return "-1"
static func sort(d:Dictionary, reverse:bool=false) -> Dictionary:
return Dict.new(d).sort(reverse)

View File

@ -0,0 +1,313 @@
[gd_scene load_steps=16 format=2]
[ext_resource path="res://addons/text_editor/file_buttons.gd" type="Script" id=1]
[ext_resource path="res://addons/text_editor/TE_TextEditor.gd" type="Script" id=2]
[ext_resource path="res://addons/text_editor/TE_FilesList.gd" type="Script" id=3]
[ext_resource path="res://addons/text_editor/TE_FileEditor.gd" type="Script" id=4]
[ext_resource path="res://addons/text_editor/TE_SymbolsList.gd" type="Script" id=5]
[ext_resource path="res://addons/text_editor/FileTabs.gd" type="Script" id=6]
[ext_resource path="res://addons/text_editor/TE_TagsPanel.gd" type="Script" id=7]
[ext_resource path="res://addons/text_editor/line_edit.gd" type="Script" id=8]
[ext_resource path="res://addons/text_editor/MetaInfo.gd" type="Script" id=9]
[ext_resource path="res://addons/text_editor/fonts/font_i.tres" type="DynamicFont" id=10]
[ext_resource path="res://addons/text_editor/fonts/font_b.tres" type="DynamicFont" id=11]
[ext_resource path="res://addons/text_editor/fonts/font_r.tres" type="DynamicFont" id=12]
[ext_resource path="res://addons/text_editor/fonts/font_bi.tres" type="DynamicFont" id=13]
[ext_resource path="res://addons/text_editor/fonts/font.tres" type="DynamicFont" id=14]
[sub_resource type="Theme" id=1]
TooltipLabel/fonts/font = ExtResource( 12 )
[node name="text_editor" type="Control"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
script = ExtResource( 2 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="file_editor" type="TextEdit" parent="."]
visible = false
anchor_right = 1.0
anchor_bottom = 1.0
margin_left = 222.0
margin_top = 74.0
margin_right = 214.0
margin_bottom = 30.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_fonts/font = ExtResource( 14 )
highlight_current_line = true
syntax_highlighting = true
show_line_numbers = true
draw_tabs = true
fold_gutter = true
highlight_all_occurrences = true
minimap_draw = true
script = ExtResource( 4 )
[node name="c" type="VBoxContainer" parent="."]
anchor_right = 1.0
anchor_bottom = 1.0
custom_constants/separation = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c" type="PanelContainer" parent="c"]
margin_right = 1024.0
margin_bottom = 34.0
[node name="c" type="HBoxContainer" parent="c/c"]
margin_left = 7.0
margin_top = 7.0
margin_right = 1017.0
margin_bottom = 27.0
script = ExtResource( 1 )
[node name="test" type="Button" parent="c/c/c"]
margin_right = 56.0
margin_bottom = 20.0
text = "update"
[node name="file_button" type="MenuButton" parent="c/c/c"]
margin_left = 60.0
margin_right = 92.0
margin_bottom = 20.0
text = "file"
items = [ "New File", null, 0, false, false, 0, 0, null, "", false, "Extensions", null, 0, false, false, 1, 0, null, "Extensions", false, "New File", null, 0, false, false, 2, 0, null, "", false, "Extensions", null, 0, false, false, 3, 0, null, "Extensions", false ]
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c3" type="HSplitContainer" parent="c"]
margin_top = 34.0
margin_right = 1024.0
margin_bottom = 600.0
size_flags_vertical = 3
split_offset = -300
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c2" type="PanelContainer" parent="c/c3"]
margin_right = 206.0
margin_bottom = 566.0
rect_min_size = Vector2( 200, 0 )
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="c" type="Panel" parent="c/c3/c2"]
margin_left = 7.0
margin_top = 7.0
margin_right = 199.0
margin_bottom = 559.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="list_files" type="RichTextLabel" parent="c/c3/c2/c"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
theme = SubResource( 1 )
custom_fonts/bold_italics_font = ExtResource( 13 )
custom_fonts/italics_font = ExtResource( 10 )
custom_fonts/bold_font = ExtResource( 11 )
custom_fonts/normal_font = ExtResource( 12 )
bbcode_enabled = true
bbcode_text = "[url=add_file:0][color=#ff00ff00]+[/color][/url]
[color=#ff4d4d4d][url=d:0][color=#ff333333][/color]🗀▼[b]test_files[/b][/url][/color]
[url=f:0][color=#ff333333][/color][color=#ff333333]┣╸[/color][i][color=#ff404040]questish[/color][/i].[color=#ff666666]yaml[/color][/url]
[url=f:1][color=#ff333333][/color][color=#ff333333]┣╸[/color][i][color=#ff404040]garoyle[/color][/i].[color=#ff666666]md[/color][/url]
[url=f:2][color=#ff333333][/color][color=#ff333333]┣╸[/color][i][color=#ff404040]json[/color][/i].[color=#ff666666]json[/color][/url]
[url=f:3][color=#ff333333][/color][color=#ff333333]┣╸[/color][i][color=#ff404040]config[/color][/i].[color=#ff666666]ini[/color][/url]
[url=f:4][color=#ff333333][/color][color=#ff333333]┣╸[/color][i][color=#ff404040]acc[/color][/i].[color=#ff666666]csv[/color][/url]
[color=#ff4d4d4d][url=d:1][color=#ff333333]┃ [/color]🗀▼[b]afolder[/b][/url][/color]
[url=f:5][color=#ff333333]┃ [/color][color=#ff333333]┣╸[/color][i][color=#ff404040]new_file[/color][/i].[color=#ff666666]yaml[/color][/url]
[url=f:6][color=#ff333333]┃ [/color][color=#ff333333]┣╸[/color][i][color=#ff404040]ass2[/color][/i].[color=#ff666666]yaml[/color][/url]
[url=f:7][color=#ff333333]┃ [/color][color=#ff333333]┣╸[/color][i][color=#ff404040]todo[/color][/i].[color=#ff666666]md[/color][/url]
[url=f:8][color=#ff333333]┃ [/color][color=#ff333333]┣╸[/color][i][color=#ff404040]my_fileok[/color][/i].[color=#ff666666]md[/color][/url]
[color=#ff4d4d4d][url=d:2][color=#ff333333]┃ ┃ [/color]🗀▼[b]New Folder[/b][/url][/color]
[url=f:9][color=#ff333333]┃ ┃ [/color][color=#ff333333]┗╸[/color][i][color=#ff404040]mor[/color][/i].[color=#ff666666]md[/color][/url]"
meta_underlined = false
text = "+
🗀▼test_files
┣╸questish.yaml
┣╸garoyle.md
┣╸json.json
┣╸config.ini
┣╸acc.csv
┃ 🗀▼afolder
┃ ┣╸new_file.yaml
┃ ┣╸ass2.yaml
┃ ┣╸todo.md
┃ ┣╸my_fileok.md
┃ ┃ 🗀▼New Folder
┃ ┃ ┗╸mor.md"
script = ExtResource( 3 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="file_popup" type="PopupMenu" parent="c/c3/c2/c/list_files"]
margin_right = 20.0
margin_bottom = 20.0
custom_fonts/font = ExtResource( 14 )
items = [ "Rename", null, 0, false, false, 0, 0, null, "", false, "", null, 0, false, false, -1, 0, null, "", true, "Remove", null, 0, false, false, 2, 0, null, "", false ]
[node name="dir_popup" type="PopupMenu" parent="c/c3/c2/c/list_files"]
margin_right = 20.0
margin_bottom = 20.0
custom_fonts/font = ExtResource( 14 )
items = [ "Create new file", null, 0, false, false, 0, 0, null, "", false ]
[node name="c" type="HSplitContainer" parent="c/c3"]
margin_left = 218.0
margin_right = 1024.0
margin_bottom = 566.0
size_flags_horizontal = 3
size_flags_vertical = 3
split_offset = -80
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c" type="VBoxContainer" parent="c/c3/c"]
margin_right = 614.0
margin_bottom = 566.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="line_edit" type="LineEdit" parent="c/c3/c/c"]
visible = false
margin_right = 614.0
margin_bottom = 24.0
custom_fonts/font = ExtResource( 12 )
script = ExtResource( 8 )
[node name="tab_container" type="TabContainer" parent="c/c3/c/c"]
margin_right = 614.0
margin_bottom = 539.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_fonts/font = ExtResource( 12 )
tab_align = 0
drag_to_rearrange_enabled = true
script = ExtResource( 6 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="meta" type="RichTextLabel" parent="c/c3/c/c"]
margin_top = 543.0
margin_right = 614.0
margin_bottom = 566.0
custom_fonts/bold_italics_font = ExtResource( 13 )
custom_fonts/italics_font = ExtResource( 10 )
custom_fonts/bold_font = ExtResource( 11 )
custom_fonts/normal_font = ExtResource( 12 )
bbcode_enabled = true
fit_content_height = true
script = ExtResource( 9 )
[node name="c2" type="PanelContainer" parent="c/c3/c"]
margin_left = 626.0
margin_right = 806.0
margin_bottom = 566.0
rect_min_size = Vector2( 100, 0 )
size_flags_vertical = 3
[node name="c" type="VSplitContainer" parent="c/c3/c/c2"]
margin_left = 7.0
margin_top = 7.0
margin_right = 173.0
margin_bottom = 559.0
custom_constants/autohide = 0
[node name="c" type="Panel" parent="c/c3/c/c2/c"]
margin_right = 166.0
margin_bottom = 270.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="list_symbols" type="RichTextLabel" parent="c/c3/c/c2/c/c"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_vertical = 3
custom_fonts/bold_italics_font = ExtResource( 13 )
custom_fonts/italics_font = ExtResource( 10 )
custom_fonts/bold_font = ExtResource( 11 )
custom_fonts/normal_font = ExtResource( 12 )
bbcode_enabled = true
bbcode_text = "[color=#ff808080][i][center]*No symbols*"
meta_underlined = false
text = "*No symbols*"
script = ExtResource( 5 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="c2" type="Panel" parent="c/c3/c/c2/c"]
margin_top = 282.0
margin_right = 166.0
margin_bottom = 552.0
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="list_tags" type="RichTextLabel" parent="c/c3/c/c2/c/c2"]
anchor_right = 1.0
anchor_bottom = 1.0
size_flags_horizontal = 3
size_flags_vertical = 3
custom_fonts/bold_italics_font = ExtResource( 13 )
custom_fonts/italics_font = ExtResource( 10 )
custom_fonts/bold_font = ExtResource( 11 )
custom_fonts/normal_font = ExtResource( 12 )
bbcode_enabled = true
bbcode_text = "[color=#ff808080][i][center]*No tags*"
meta_underlined = false
text = "*No tags*"
script = ExtResource( 7 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="popup" type="ConfirmationDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -100.0
margin_top = -35.0
margin_right = 100.0
margin_bottom = 35.0
[node name="popup_unsaved" type="ConfirmationDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -100.0
margin_top = -35.0
margin_right = 100.0
margin_bottom = 35.0
window_title = "Warning"
dialog_text = "Unsaved data will be lost."
[node name="file_dialog" type="FileDialog" parent="."]
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
margin_left = -297.5
margin_top = -157.0
margin_right = 297.5
margin_bottom = 157.0
current_dir = "res://test_files"
current_path = "res://test_files/"
__meta__ = {
"_edit_use_anchors_": false
}

View File

@ -1,3 +1,4 @@
tool
extends Resource
class_name TE_ExtensionHelper
@ -7,10 +8,11 @@ func generate_meta(t:TextEdit, r:TE_RichTextLabel):
var chars = TE_Util.commas(len(t.text))
var words = TE_Util.commas(len(t.text.split(" ", false)))
var lines = TE_Util.commas(len(TE_Util.split_many(t.text, ".?!\n", false)))
r.add_constant_override("table_hseparation", int(r.rect_size.x / 4.0))
var bytes = TE_Util.file_size(t.file_path)
r.add_constant_override("table_hseparation", int(r.rect_size.x / 5.0))
r.set_bbcode(r.table([
["chars", "words", "lines"],
[chars, words, lines]
["chars", "words", "lines", "bytes"],
[chars, words, lines, bytes]
]))
func toggle_comment(t:TextEdit, head:String="", tail:String=""):

View File

@ -0,0 +1,2 @@
tool
extends "res://addons/text_editor/ext/ext_ini.gd"

View File

@ -1,2 +1,3 @@
tool
extends TE_ExtensionHelper

View File

@ -1,3 +1,4 @@
tool
extends TE_ExtensionHelper
func apply_colors(e:TextEditor, t:TextEdit):

View File

@ -1,3 +1,4 @@
tool
extends TE_ExtensionHelper
func toggle_comment(t:TextEdit, head:String="/*", tail:String="*/"):

View File

@ -1,3 +1,4 @@
tool
extends TE_ExtensionHelper
func toggle_comment(t:TextEdit, head:String="<!-- ", tail:String=" -->"):
@ -5,17 +6,17 @@ func toggle_comment(t:TextEdit, head:String="<!-- ", tail:String=" -->"):
func apply_colors(e:TextEditor, t:TextEdit):
.apply_colors(e, t)
var code:Color = Color.aquamarine.darkened(.5)
var code:Color = e.color_text.darkened(.5)
t.add_keyword_color("true", e.color_var)
t.add_keyword_color("false", e.color_var)
# bold italic
t.add_color_region("***", "***", Color.tomato.lightened(.3), false)
t.add_color_region("***", "***", Color.tomato.darkened(.3), false)
# bold
t.add_color_region("**", "**", Color.tomato, false)
# italic
t.add_color_region("*", "*", Color.tomato.darkened(.3), false)
t.add_color_region("*", "*", Color.tomato.lightened(.3), false)
# quote
t.add_color_region("> ", "", Color.white.darkened(.6), true)

View File

@ -1,3 +1,4 @@
tool
extends TE_ExtensionHelper
func _is_commented(lines) -> bool:

View File

@ -1,3 +1,4 @@
tool
extends LineEdit
onready var editor:TextEditor = owner

View File

@ -1,267 +0,0 @@
extends RichTextLabel
onready var editor:TextEditor = owner
onready var popup:PopupMenu = $popup
onready var drag_label:RichTextLabel = $drag_label
var files:Array = []
var dirs:Array = []
var selected
var hovered:String = ""
var dragging:String = ""
var drag_start:Vector2
func _ready():
var _e
_e = editor.connect("updated_file_list", self, "_redraw")
_e = editor.connect("tags_updated", self, "_redraw")
_e = editor.connect("file_opened", self, "_file_opened")
_e = editor.connect("file_closed", self, "_file_closed")
_e = editor.connect("file_selected", self, "_file_selected")
_e = editor.connect("file_renamed", self, "_file_renamed")
# _e = connect("meta_clicked", self, "_clicked")
_e = connect("meta_hover_started", self, "_meta_entered")
_e = connect("meta_hover_ended", self, "_meta_exited")
# popup
popup.add_item("Rename")
popup.add_separator()
popup.add_item("Remove")
_e = popup.connect("index_pressed", self, "_popup")
popup.add_font_override("font", TextEditor.FONT)
for n in [self, drag_label]:
n.add_font_override("normal_font", editor.FONT_R)
n.add_font_override("bold_font", editor.FONT_B)
n.add_font_override("italics_font", editor.FONT_I)
n.add_font_override("bold_italics_font", editor.FONT_BI)
set_process(false)
func _popup(index:int):
var p = selected.split(":", true, 1)
var type = p[0]
var file = files[int(p[1])] if type == "f" else dirs[int(p[1])] if type == "d" else ""
match popup.get_item_text(index):
"Rename":
var fname:String = selected.file_path.get_file()
var i:int = fname.find(".")
editor.line_edit.display(fname, self, "_renamed")
editor.line_edit.select(0, i)
"Remove":
if type == "f":
editor.recycle_file(file)
_:
selected = {}
func _renamed(new_file:String):
var old_path:String = selected.file_path
var old_file:String = old_path.get_file()
if new_file != old_file:
var new_path:String = old_path.get_base_dir().plus_file(new_file)
editor.rename_file(old_path, new_path)
selected = {}
func _process(_delta):
var mp = get_global_mouse_position()
if mp.distance_to(drag_start) > 16:
drag_label.visible = true
drag_label.set_global_position(mp)
func end_drag():
dragging = ""
drag_label.visible = false
set_process(false)
func _input(e:InputEvent):
if e is InputEventMouseButton and hovered:
var m = hovered.split(":", true, 1)
var type = m[0]
var index = int(m[1])
if e.button_index == BUTTON_LEFT:
if e.pressed:
dragging = hovered
if type == "f":
drag_label.set_bbcode(files[index].get_file())
# drag_label.visible = true
drag_start = get_global_mouse_position()
set_process(true)
else:
if dragging and dragging != hovered:
m = dragging.split(":", true, 1)
var drag_type = m[0]
var drag_index = int(m[1])
if drag_type == "f" and type == "d":
var dir:String = dirs[index].file_path
var old_path:String = files[drag_index]
var new_path:String = dir.plus_file(old_path.get_file())
editor.rename_file(old_path, new_path)
else:
if type == "d":
dirs[index].open = not dirs[index].open
_redraw()
elif type == "f":
editor.select_file(files[index])
end_drag()
get_tree().set_input_as_handled()
return
elif e.button_index == BUTTON_RIGHT:
if e.pressed:
selected = hovered
popup.set_global_position(get_global_mouse_position())
popup.popup()
get_tree().set_input_as_handled()
return
if e is InputEventMouseButton:
if dragging and (e.button_index == BUTTON_LEFT and not e.pressed) or (e.button_index == BUTTON_RIGHT):
end_drag()
get_tree().set_input_as_handled()
return
func _meta_entered(m): hovered = m
func _meta_exited(_m): hovered = ""
func _file_opened(_file_path:String): _redraw()
func _file_closed(_file_path:String): _redraw()
func _file_selected(_file_path:String): _redraw()
func _file_renamed(_op:String, _np:String): _redraw()
#func updated_file_list():
# items.clear()
# _updated_file_list(editor.file_list, 0)
# redraw()
var lines:PoolStringArray = PoolStringArray()
func _redraw():
lines = PoolStringArray()
lines.append("[url=add_file:0][color=#%s]+[/color][/url]" % [Color.green.to_html()])
dirs.clear()
_draw_dir(editor.file_list[""], 0)
set_bbcode(lines.join("\n"))
func clr(s:String, c:Color) -> String: return "[color=#%s]%s[/color]" % [c.to_html(), s]
func i(s:String) -> String: return "[i]%s[/i]" % s
func b(s:String) -> String: return "[b]%s[/b]" % s
func url(s:String, url:String) -> String: return "[url=%s]%s[/url]" % [url, s]
const FOLDER:String = "🗀" # not visible in godot
func _draw_dir(dir:Dictionary, deep:int):
var space = clr("".repeat(deep), Color.white.darkened(.8))
var file:String = dir.file_path
var name:String = b(file.get_file())
var head:String = "" if dir.open else ""
var link:String = url("%s%s%s%s" % [space, FOLDER, head, name], "d:%s" % len(dirs))
lines.append(clr(link, Color.white.darkened(.7)))
dirs.append(dir)
# var add = "[url=add_file:%s][color=#%s]+[/color][/url]" % [dindex, Color.green.to_html()]
# name = "[color=#%s]%s[/color] %s" % [Color.darkslategray.to_html(), item.name, add]
var sel = editor.get_selected_tab()
sel = sel.file_path if sel else ""
if dir.open:
var i = 0
var last = len(dir.files)-1
for path in dir.files:
var file_path = dir.files[path]
# dir
if file_path is Dictionary:
_draw_dir(file_path, deep+1)
# file
else:
file = path.get_file()
var is_selected = file_path == sel
head = "┣╸" if i != last else "┗╸"
if is_selected:
head = clr(head, Color.white.darkened(.5))
else:
head = clr(head, Color.white.darkened(.8))
var p = file.split(".", true, 1)
file = p[0]
var color = Color.white if editor.is_tagged(file_path) else Color.white.darkened(.5)
if editor.is_selected(file_path):
file = clr(file, color)
elif editor.is_opened(file_path):
file = clr(file, color.darkened(.5))
else:
file = i(clr(file, color.darkened(.75)))
var ext = clr(p[1], Color.white.darkened(.6))
var line = space + head + file + "." + ext
lines.append(url(line, "f:%s" % len(files)))
files.append(file_path)
i += 1
# file
# else:
# var p = item.name.split(".", true, 1)
# name = p[0]
# var ext = p[1]
# var clr = Color.white
# var dim = .75
#
# if editor.is_selected(item.file_path):
# dim = 0.0
#
# elif editor.is_open(item.file_path):
# dim = .5
#
# name = "[color=#%s]%s[/color]" % [clr.darkened(dim).to_html(), name]
# name += "[color=#%s].%s[/color]" % [clr.darkened(.75).to_html(), ext]
#
# var tab = editor.get_tab(item.file_path)
# if tab and editor.is_tagged_or_visible(tab.tags.keys()):
# name = "[b]%s[/b]" % name
#
# var space = ""
# if item.deep:
# wide += item.deep * 2
#
# if item.deep > 1:
# space += "┃ " + " ".repeat(int(max(0, item.deep-2)))
# else:
# space += " ".repeat(int(max(0, item.deep-1)))
#
# if item.last:
# space += "┗╸"
# else:
# space += "┣╸"
#
# space = "[color=#%s]%s[/color]" % [Color.darkslategray.to_html(), space]
#
# # add extra space to make clicking easier
# var extra = max(0, 16 - wide)
# name += " ".repeat(extra)
# text.append("%s[url=f:%s]%s[/url]" % [space, i, name])
#
# set_bbcode(text.join("\n"))
#func _updated_file_list(data:Dictionary, deep:int):
# var total = len(data)
# var i = 0
# for k in data:
# if data[k] is Dictionary:
# items.append({ last=i==total-1, type="D", name=k, file_path=data[k].file_path, deep=deep, open=true })
# _updated_file_list(data[k].files, deep+1)
# else:
# items.append({ last=i==total-1, type="F", name=k, file_path=data[k], deep=deep })
# i += 1

View File

@ -0,0 +1,7 @@
[plugin]
name="TextEditor"
description="A text editor for Godot."
author="teebar"
version="1.0"
script="plugin.gd"

View File

@ -0,0 +1,26 @@
tool
extends EditorPlugin
const TEPanel:PackedScene = preload("res://addons/text_editor/TextEditor.tscn")
var panel:Node
func get_plugin_name(): return "Text"
func get_plugin_icon(): return get_editor_interface().get_base_control().get_icon("Font", "EditorIcons")
func has_main_screen(): return true
func _enter_tree():
panel = TEPanel.instance()
panel.plugin = self
panel.plugin_hint = true
get_editor_interface().get_editor_viewport().add_child(panel)
make_visible(false)
func _exit_tree():
if panel:
panel.queue_free()
func make_visible(visible):
if panel:
panel.visible = visible

View File

@ -1,5 +1,8 @@
tool
extends TabContainer
onready var editor:TextEditor = owner
var mouse:bool = false
func _ready():
@ -8,6 +11,9 @@ func _ready():
_e = connect("mouse_exited", self, "set", ["mouse", false])
func _input(e):
if not editor.is_plugin_active():
return
if mouse and e is InputEventMouseButton and e.pressed:
if e.button_index == BUTTON_WHEEL_DOWN:
prev()

View File

@ -24,10 +24,10 @@ _global_script_classes=[ {
"language": "GDScript",
"path": "res://addons/text_editor/TE_Util.gd"
}, {
"base": "Node",
"base": "Control",
"class": "TextEditor",
"language": "GDScript",
"path": "res://addons/text_editor/TextEditor.gd"
"path": "res://addons/text_editor/TE_TextEditor.gd"
} ]
_global_script_class_icons={
"TE_ExtensionHelper": "",
@ -39,7 +39,7 @@ _global_script_class_icons={
[application]
config/name="TextEdit"
run/main_scene="res://TextEditor.tscn"
run/main_scene="res://addons/text_editor/TextEditor.tscn"
config/icon="res://icon.png"
[physics]