diff --git a/app/components/Editor.gdns b/app/components/Editor.gdns new file mode 100644 index 00000000..0bc91ee5 --- /dev/null +++ b/app/components/Editor.gdns @@ -0,0 +1,8 @@ +[gd_resource type="NativeScript" load_steps=2 format=2] + +[ext_resource path="res://nimlib.gdnlib" type="GDNativeLibrary" id=1] + +[resource] +resource_name = "Editor" +class_name = "Editor" +library = ExtResource( 1 ) diff --git a/app/components/Editor.tscn b/app/components/Editor.tscn index 4de0ee18..e001f524 100644 --- a/app/components/Editor.tscn +++ b/app/components/Editor.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=5 format=2] -[ext_resource path="res://components/TextEdit.gdns" type="Script" id=1] +[ext_resource path="res://components/Editor.gdns" type="Script" id=1] [ext_resource path="res://themes/DarkTheme.tres" type="Theme" id=2] [ext_resource path="res://components/FloatingButton.gdns" type="Script" id=3] @@ -9,9 +9,24 @@ bg_color = Color( 0, 0, 0, 0.839216 ) border_width_right = 2 border_color = Color( 0.964706, 0.952941, 0.909804, 1 ) -[node name="Editor" type="TextEdit"] -margin_right = 961.0 -margin_bottom = 1080.0 +[node name="MarginContainer" type="MarginContainer"] +margin_right = 40.0 +margin_bottom = 40.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource( 1 ) + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +margin_right = 74.0 +margin_bottom = 95.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="TextEdit" type="TextEdit" parent="ScrollContainer"] +margin_right = 74.0 +margin_bottom = 95.0 +mouse_filter = 1 +size_flags_horizontal = 3 size_flags_vertical = 3 theme = ExtResource( 2 ) custom_colors/selection_color = Color( 0.207843, 0, 0.321569, 1 ) @@ -29,40 +44,37 @@ smooth_scrolling = true wrap_enabled = true caret_blink = true caret_moving_by_right_click = false -script = ExtResource( 1 ) [node name="GridContainer" type="GridContainer" parent="."] -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_right = 14.0 -margin_bottom = 55.0 -mouse_filter = 0 +margin_right = 74.0 +margin_bottom = 95.0 +mouse_filter = 2 columns = 2 [node name="Label" type="Label" parent="GridContainer"] -margin_right = 881.0 -margin_bottom = 111.0 +margin_bottom = 95.0 size_flags_horizontal = 3 size_flags_vertical = 1 [node name="MarginContainer" type="MarginContainer" parent="GridContainer"] -margin_left = 885.0 -margin_right = 975.0 -margin_bottom = 111.0 +margin_left = 4.0 +margin_right = 74.0 +margin_bottom = 95.0 +mouse_filter = 2 custom_constants/margin_right = 50 custom_constants/margin_top = 25 [node name="GridContainer" type="GridContainer" parent="GridContainer/MarginContainer"] margin_top = 25.0 -margin_right = 40.0 -margin_bottom = 111.0 +margin_right = 20.0 +margin_bottom = 95.0 rect_pivot_offset = Vector2( -911, -78 ) -mouse_filter = 0 +mouse_filter = 2 custom_constants/vseparation = 30 [node name="Close" type="Button" parent="GridContainer/MarginContainer/GridContainer"] -margin_right = 40.0 -margin_bottom = 28.0 +margin_right = 20.0 +margin_bottom = 20.0 rect_pivot_offset = Vector2( 138, 238 ) mouse_default_cursor_shape = 2 theme_type_variation = "IconButton" @@ -70,9 +82,9 @@ text = "  " script = ExtResource( 3 ) [node name="Run" type="Button" parent="GridContainer/MarginContainer/GridContainer"] -margin_top = 58.0 -margin_right = 40.0 -margin_bottom = 86.0 +margin_top = 50.0 +margin_right = 20.0 +margin_bottom = 70.0 mouse_filter = 1 mouse_default_cursor_shape = 2 theme_type_variation = "IconButton" diff --git a/app/components/MarkdownLabel.tscn b/app/components/MarkdownLabel.tscn index b6cb50b5..956eb664 100644 --- a/app/components/MarkdownLabel.tscn +++ b/app/components/MarkdownLabel.tscn @@ -67,7 +67,6 @@ mono_font = SubResource( 12 ) [node name="VBoxContainer" type="VBoxContainer" parent="."] margin_right = 1920.0 margin_bottom = 1080.0 -mouse_filter = 0 size_flags_horizontal = 3 size_flags_vertical = 3 custom_constants/separation = 0 @@ -77,6 +76,7 @@ visible = false margin_bottom = 69.0 rect_clip_content = false focus_mode = 2 +mouse_filter = 1 size_flags_horizontal = 3 theme = ExtResource( 2 ) custom_colors/default_color = Color( 1, 1, 1, 1 ) @@ -92,6 +92,7 @@ __meta__ = { [node name="TextEdit" type="TextEdit" parent="VBoxContainer"] visible = false +mouse_filter = 1 custom_colors/selection_color = Color( 0.207843, 0, 0.321569, 1 ) custom_colors/executing_line_color = Color( 0.0392157, 0, 0.168627, 1 ) custom_colors/font_color_readonly = Color( 0.878431, 0.878431, 0.878431, 1 ) diff --git a/app/scenes/GUI.tscn b/app/scenes/GUI.tscn index a2962146..73ae1de7 100644 --- a/app/scenes/GUI.tscn +++ b/app/scenes/GUI.tscn @@ -135,20 +135,22 @@ custom_constants/separation = 2 margin_right = 961.0 margin_bottom = 678.0 mouse_filter = 2 +size_flags_horizontal = 3 size_flags_vertical = 3 size_flags_stretch_ratio = 2.0 [node name="Editor" parent="LeftPanel/ThemeHolder/MarginContainer" instance=ExtResource( 1 )] visible = false +margin_right = 961.0 margin_bottom = 678.0 -middle_mouse_paste_enabled = false -deselect_on_focus_loss_enabled = false -drag_and_drop_selection_enabled = false +mouse_filter = 1 [node name="Console" parent="LeftPanel/ThemeHolder" instance=ExtResource( 2 )] margin_top = 680.0 margin_right = 961.0 margin_bottom = 1080.0 +size_flags_horizontal = 1 +size_flags_vertical = 1 [node name="RightPanel" type="MarginContainer" parent="."] visible = false diff --git a/src/game.nim b/src/game.nim index 5625a64a..b2883216 100644 --- a/src/game.nim +++ b/src/game.nim @@ -475,8 +475,9 @@ gdobj Game of Node: if event.is_action_pressed("previous"): state.update_action_index(-1) - # NOTE: alt+enter isn't being picked up on windows if the editor is - # open. Needs investigation. + + # NOTE: alt+enter isn't being picked up on windows if the editor is + # open. Needs investigation. if event.is_action_pressed("toggle_fullscreen") or ( host_os == "windows" and CommandMode in state.local_flags and EditorVisible in state.local_flags and event of InputEventKey and diff --git a/src/ui/console.nim b/src/ui/console.nim index a93055ec..85e8c247 100644 --- a/src/ui/console.nim +++ b/src/ui/console.nim @@ -16,37 +16,42 @@ gdobj Console of RichTextLabel: let width = self.rect_size.x self.rect_position = vec2(width * offset, self.rect_position.y) + proc show() = + self.opacity = 1.0 + if ?self.tween: + self.tween.kill + self.tween = self.get_tree.create_tween + self.visible = true + discard + self.tween + .tween_method( + self, "_offset_x", -1.0.to_variant, 0.0.to_variant, animation_duration + ) + .set_trans(TRANS_EXPO) + .set_ease(EASE_IN_OUT) + + proc hide() = + if ?self.tween: + self.tween.kill + self.tween = self.get_tree.create_tween + self.rect_position = vec2(0.0, self.rect_position.y) + discard + self.tween + .tween_method( + self, "_offset_x", 0.0.to_variant, -1.0.to_variant, animation_duration + ) + .set_trans(TRANS_EXPO) + .set_ease(EASE_IN_OUT) + discard self.tween.tween_callback( + self, "set_visible", new_array(false.to_variant) + ) + proc init*() = state.local_flags.changes: if ConsoleVisible.added: - if ?self.tween: - self.tween.kill - self.tween = self.get_tree.create_tween - self.visible = true - discard - self.tween - .tween_method( - self, "_offset_x", -1.0.to_variant, 0.0.to_variant, - animation_duration - ) - .set_trans(TRANS_EXPO) - .set_ease(EASE_IN_OUT) + self.show() elif ConsoleVisible.removed: - if ?self.tween: - self.tween.kill - self.tween = self.get_tree.create_tween - self.rect_position = vec2(0.0, self.rect_position.y) - discard - self.tween - .tween_method( - self, "_offset_x", 0.0.to_variant, -1.0.to_variant, - animation_duration - ) - .set_trans(TRANS_EXPO) - .set_ease(EASE_IN_OUT) - discard self.tween.tween_callback( - self, "set_visible", new_array(false.to_variant) - ) + self.hide() elif CommandMode.added: self.ghost() elif CommandMode.removed: @@ -72,14 +77,8 @@ gdobj Console of RichTextLabel: state.nodes.game.bind_signal(self, "gui_input", self.name) if ConsoleVisible notin state.local_flags: - let tween = self.get_tree.create_tween self.opacity = 0.0 - discard tween.tween_property( - self, "rect_position:y", (self.rect_size.y).to_variant, 0.0 - ) - discard - tween.tween_callback(self, "set_visible", new_array(false.to_variant)) - discard tween.tween_property(self, "modulate:a", 1.0.to_variant, 0.0) + self.hide() for child in self.get_children(): let o = child.as_object(Node) as VScrollBar diff --git a/src/ui/editor.nim b/src/ui/editor.nim index 5c37663e..460b0779 100644 --- a/src/ui/editor.nim +++ b/src/ui/editor.nim @@ -19,27 +19,33 @@ proc configure_highlighting*(self: TextEdit) = # line comments self.add_color_region("#", "\n", ir_black[comment], true) -gdobj Editor of TextEdit: +gdobj Editor of MarginContainer: var comment_color* {.gdExport.} = init_color(0.5, 0.5, 0.5) og_bg_color: Color tween: SceneTreeTween + scroll_bar: VScrollBar + text_edit: TextEdit proc indent_new_line() = - let column = int self.cursor_get_column - 1 + let editor = self.text_edit + let column = int editor.cursor_get_column - 1 if column > 0: let - line = self.get_line(self.cursor_get_line)[0 .. column] - stripped = line.strip() + line = editor.get_line(editor.cursor_get_line)[0 .. column] + stripped = line.strip if stripped.high > 0: - let last = $stripped[stripped.high] + let last = stripped[stripped.high] var spaces = line.indentation - if (stripped in ["var", "let", "const", "type"]) or last in [":", "="]: + if (stripped in ["var", "let", "const", "type"]) or last in [':', '=']: spaces += 2 - self.insert_text_at_cursor("\n" & " ".repeat(spaces)) + editor.insert_text_at_cursor("\n" & " ".repeat(spaces)) + self.get_tree.set_input_as_handled() + elif stripped.len == 0 and line.len > 0: + editor.insert_text_at_cursor("\n" & line) self.get_tree.set_input_as_handled() method input*(event: InputEvent) = @@ -48,13 +54,15 @@ gdobj Editor of TextEdit: if event.scancode == KEY_ENTER and host_os != "ios": self.indent_new_line() if event.scancode == KEY_SEMICOLON and state.config.semicolon_as_colon: - self.insert_text_at_cursor(":") + self.text_edit.insert_text_at_cursor(":") self.get_tree.set_input_as_handled() elif event.scancode == KEY_HOME: - self.cursor_set_column(0) + self.text_edit.cursor_set_column(0) self.get_tree.set_input_as_handled() elif event.scancode == KEY_END: - self.cursor_set_column self.get_line(self.cursor_get_line).len + self.text_edit.cursor_set_column self.text_edit.get_line( + self.text_edit.cursor_get_line + ).len self.get_tree.set_input_as_handled() method unhandled_input*(event: InputEvent) = @@ -62,32 +70,32 @@ gdobj Editor of TextEdit: event.is_action_pressed("ui_cancel"): if not (event of InputEventJoypadButton) or CommandMode notin state.local_flags: - state.open_unit.code = Code.init(self.text) + state.open_unit.code = Code.init(self.text_edit.text) state.open_unit = nil self.get_tree().set_input_as_handled() proc clear_errors() = - for i in 0 ..< self.get_line_count(): - self.set_line_as_marked(i, false) + for i in 0 ..< self.text_edit.get_line_count(): + self.text_edit.set_line_as_marked(i, false) proc highlight_errors() = - self.clear_executing_line() + self.text_edit.clear_executing_line() if ?state.open_unit: for err in state.open_unit.errors: - self.set_line_as_marked(int64(err.info.line - 1), true) + self.text_edit.set_line_as_marked(int64(err.info.line - 1), true) proc `executing_line=`*(line: int) = - if self.get_line_count >= line and line >= 0: - self.set_executing_line(line) + if self.text_edit.get_line_count >= line and line >= 0: + self.text_edit.set_executing_line(line) else: - self.clear_executing_line() + self.text_edit.clear_executing_line() method on_text_changed*() = - state.player.open_code = self.text + state.player.open_code = self.text_edit.text method on_cursor_changed*() = state.player.cursor_position = ( - int self.cursor_get_line, int self.cursor_get_column + int self.text_edit.cursor_get_line, int self.text_edit.cursor_get_column ) method set_opacity*(opacity: float) {.gdexport.} = @@ -97,91 +105,70 @@ gdobj Editor of TextEdit: let width = self.rect_size.x self.rect_position = vec2(width * offset, self.rect_position.y) - method ghost_me*() {.gdexport.} = - self.ghost() - - method ready*() = - self.bind_signals(self, "text_changed", "cursor_changed") - state.nodes.game.bind_signal(self, "gui_input", self.name) - - for name in ["Close", "Run"]: - let control = find(name, Control) - self.bind_signal(control, ("pressed", name.to_lower)) - self.bind_signal(control, ("gui_input", "child_focused")) - - var stylebox = self.get_stylebox("normal").as(StyleBoxFlat) - self.og_bg_color = stylebox.bg_color - - state.local_flags.changes: - if ConsoleVisible.added: - self.highlight_errors() - elif ConsoleVisible.removed: - self.clear_errors() - elif EditorFocused.added: - self.grab_focus + method ghost*() {.gdexport.} = + self.text_edit.ghost() + + method unghost*() {.gdexport.} = + self.text_edit.unghost() + self.text_edit.mouse_filter = MOUSE_FILTER_PASS + + proc close_editor() = + self.text_edit.release_focus() + self.rect_position = vec2(0, 0) + if ?self.tween: + self.tween.kill + self.tween = self.get_tree.create_tween() + discard + self.tween + .tween_method( + self, "_offset_x", 0.0.to_variant, -1.0.to_variant, animation_duration + ) + .set_trans(TRANS_EXPO) + .set_ease(EASE_IN_OUT) + discard self.tween.tween_callback( + self, "set_visible", new_array(false.to_variant) + ) + proc open_editor() = + self.opacity = 0.0 + if ?self.tween: + self.tween.kill + self.tween = self.get_tree.create_tween() + self.visible = true + discard + self.tween.tween_callback(self, "_offset_x", new_array(0.0.to_variant)) + discard self.tween.tween_property(self, "modulate:a", 1.0.to_variant, 0.0) + if CommandMode in state.local_flags: + discard self.tween.tween_callback(self.text_edit, "_ghost") + discard + self.tween + .tween_method( + self, "_offset_x", -1.0.to_variant, 0.0.to_variant, animation_duration + ) + .set_trans(TRANS_EXPO) + .set_ease(EASE_IN_OUT) + + proc watch_open_unit() = var line_zid: ZID state.open_unit_value.changes: if removed: let unit = state.open_unit if unit.is_nil: - self.release_focus() - self.rect_position = vec2(0, 0) - if ?self.tween: - self.tween.kill - self.tween = self.get_tree.create_tween() - discard - self.tween - .tween_method( - self, "_offset_x", 0.0.to_variant, -1.0.to_variant, - animation_duration - ) - .set_trans(TRANS_EXPO) - .set_ease(EASE_IN_OUT) - discard self.tween.tween_callback( - self, "set_visible", new_array(false.to_variant) - ) - + Zen.thread_ctx.untrack(line_zid) + self.close_editor() state.player.open_code = "" else: + self.open_editor() line_zid = unit.current_line_value.changes: if added: # only update the executing line if the code hasn't been changed. - if self.text == state.open_unit.code.nim: + if self.text_edit.text == state.open_unit.code.nim: self.executing_line = change.item - 1 else: - self.clear_executing_line() - self.visible = true - - # self.rect_position = vec2(-960, 0) - - if change.item == nil: - self.opacity = 0.0 - if ?self.tween: - self.tween.kill - self.tween = self.get_tree.create_tween() - - discard self.tween.tween_callback( - self, "_offset_x", new_array(0.0.to_variant) - ) - discard - self.tween.tween_property(self, "modulate:a", 1.0.to_variant, 0.0) - if CommandMode in state.local_flags: - discard self.tween.tween_callback(self, "_ghost_me") - discard - self.tween - .tween_method( - self, "_offset_x", -1.0.to_variant, 0.0.to_variant, - animation_duration - ) - .set_trans(TRANS_EXPO) - .set_ease(EASE_IN_OUT) - - # tween.interpolate_value( - # 0.0.as_variant, 0.2.as_variant, 0.0, 0.2, TRANS_EXPO, EASE_IN_OUT - # ) - self.text = state.open_unit.code.nim - state.player.open_code = self.text + self.text_edit.clear_executing_line() + + self.text_edit.text = state.open_unit.code.nim + state.player.open_code = self.text_edit.text if CommandMode in state.local_flags: self.ghost() @@ -191,40 +178,75 @@ gdobj Editor of TextEdit: self.highlight_errors() let line = unit.current_line - 1 self.executing_line = line - if removed: - if ?change.item: - Zen.thread_ctx.untrack(line_zid) + proc watch_local_flags() = state.local_flags.changes: - if EditorFocused.added: - self.grab_focus + if ConsoleVisible.added: + self.highlight_errors() + elif ConsoleVisible.removed: + self.clear_errors() + elif EditorFocused.added: + self.text_edit.grab_focus if CommandMode.added: if EditorVisible in state.local_flags: - state.open_unit.code = Code.init(self.text) + state.open_unit.code = Code.init(self.text_edit.text) self.ghost() - self.release_focus + self.text_edit.release_focus() elif CommandMode.removed: if EditorVisible in state.local_flags: self.unghost() - self.grab_focus + self.text_edit.grab_focus() + + proc watch() = + self.watch_open_unit() + self.watch_local_flags() + + method ready*() = + self.text_edit = find("TextEdit", TextEdit) + self.bind_signals(self.text_edit, "text_changed", "cursor_changed") + # self.text_edit.bind_signal(self, "gui_input") + state.nodes.game.bind_signal(self, "gui_input", self.name) + + for name in ["Close", "Run"]: + let control = find(name, Control) + self.bind_signal(control, ("pressed", name.to_lower)) + self.bind_signal(control, ("gui_input", "child_focused")) + var stylebox = self.text_edit.get_stylebox("normal").as(StyleBoxFlat) + self.og_bg_color = stylebox.bg_color + + self.text_edit.configure_highlighting() - self.configure_highlighting() # hide verticle scrollbar. Should be restyled and re-enabled in the future. - for child in self.get_children(): + for child in self.text_edit.get_children(): let o = child.as_object(Node) as VScrollBar if ?o: + self.scroll_bar = o o.modulate = Color(r: 1.0, g: 1.0, b: 1.0, a: 0.0) + assert ?self.scroll_bar + self.watch() + + proc content_height(): int = + let line_height = self.text_edit.get_line_height + for line in 0 ..< self.text_edit.get_line_count: + result += int( + (self.text_edit.get_line_wrap_count(line) + 1) * line_height + ) + + method process(delta: float) = + self.text_edit.rect_min_size = + vec2(self.rect_size.x, max(float self.content_height, self.rect_size.y)) + self.text_edit.rect_size = self.text_edit.rect_min_size method on_close() = if ?state.open_unit: - state.open_unit.code = Code.init(self.text) + state.open_unit.code = Code.init(self.text_edit.text) state.open_unit = nil method on_run() = if ?state.open_unit: state.open_unit.code = Code.init("") - state.open_unit.code = Code.init(self.text) + state.open_unit.code = Code.init(self.text_edit.text) method on_child_focused(event: InputEvent) = self.grab_focus()