diff --git a/src/core/core_widget.py b/src/core/core_widget.py index c319894..81966df 100644 --- a/src/core/core_widget.py +++ b/src/core/core_widget.py @@ -7,7 +7,7 @@ from gi.repository import Gtk # Application imports from .widgets.base.banner_controls import BannerControls -from .widgets.base.editer_notebook import EditorNotebook +from .widgets.base.notebook.editor_notebook import EditorNotebook from .widgets.base.bottom_status_info_widget import BottomStatusInfoWidget diff --git a/src/core/widgets/base/notebook/__init__.py b/src/core/widgets/base/notebook/__init__.py new file mode 100644 index 0000000..dc54ccc --- /dev/null +++ b/src/core/widgets/base/notebook/__init__.py @@ -0,0 +1,3 @@ +""" + Notebook Module +""" diff --git a/src/core/widgets/base/notebook/editor_controller.py b/src/core/widgets/base/notebook/editor_controller.py new file mode 100644 index 0000000..c2bc61c --- /dev/null +++ b/src/core/widgets/base/notebook/editor_controller.py @@ -0,0 +1,32 @@ +# Python imports + +# Lib imports + +# Application imports + + + +class EditorControllerMixin: + def action_controller(self, action = "", query = ""): + page_num = self.get_current_page() + container = self.get_nth_page( page_num ) + source_view = container.get_source_view() + + if action == "do_text_search": + self.do_text_search(source_view, query) + if action == "set_buffer_language": + self.set_buffer_language(source_view, query) + if action == "set_buffer_style": + self.set_buffer_style(source_view, query) + if action == "toggle_highlight_line": + self.toggle_highlight_line(source_view) + if action == "scale_up_text": + self.scale_up_text(source_view) + if action == "scale_down_text": + self.scale_down_text(source_view) + if action == "close_tab": + self.close_tab(None, container, source_view) + if action == "keyboard_prev_tab": + self.keyboard_prev_tab(page_num) + if action == "keyboard_next_tab": + self.keyboard_next_tab(page_num) diff --git a/src/core/widgets/base/notebook/editor_events.py b/src/core/widgets/base/notebook/editor_events.py new file mode 100644 index 0000000..bbfa8ae --- /dev/null +++ b/src/core/widgets/base/notebook/editor_events.py @@ -0,0 +1,64 @@ +# Python imports + +# Lib imports + +# Application imports + + + +class EditorEventsMixin: + def _toggle_highlight_line(self): + self.action_controller("toggle_highlight_line") + + def _keyboard_close_tab(self): + self.action_controller("close_tab") + + def _keyboard_create_tab(self, _gfile): + self.create_view(gfile=_gfile) + + def _keyboard_next_tab(self): + self.action_controller("keyboard_next_tab") + + def _keyboard_prev_tab(self): + self.action_controller("keyboard_prev_tab") + + def _keyboard_scale_up_text(self): + self.action_controller("scale_up_text") + + def _keyboard_scale_down_text(self): + self.action_controller("scale_down_text") + + def _keyboard_save_file(self): + ... + + def _keyboard_save_file_as(self): + ... + + def _text_search(self, widget = None, eve = None): + self.action_controller("do_text_search", widget.get_text()) + + def do_text_search(self, query = ""): + source_view.scale_down_text() + + def set_buffer_language(self, source_view, language = "python3"): + source_view.set_buffer_language(language) + + def set_buffer_style(self, source_view, style = "tango"): + source_view.set_buffer_style(style) + + def keyboard_prev_tab(self, page_num): + page_num = self.get_n_pages() - 1 if page_num == 0 else page_num - 1 + self.set_current_page(page_num) + + def keyboard_next_tab(self, page_num): + page_num = 0 if self.get_n_pages() - 1 == page_num else page_num + 1 + self.set_current_page(page_num) + + def scale_up_text(self, source_view): + source_view.scale_up_text() + + def scale_down_text(self, source_view): + source_view.scale_down_text() + + def toggle_highlight_line(self, source_view): + source_view.toggle_highlight_line() diff --git a/src/core/widgets/base/editer_notebook.py b/src/core/widgets/base/notebook/editor_notebook.py similarity index 55% rename from src/core/widgets/base/editer_notebook.py rename to src/core/widgets/base/notebook/editor_notebook.py index 10418ee..41300ec 100644 --- a/src/core/widgets/base/editer_notebook.py +++ b/src/core/widgets/base/notebook/editor_notebook.py @@ -9,11 +9,15 @@ from gi.repository import Gdk from gi.repository import Gio # Application imports -from .sourceview_container import SourceViewContainer +from ..sourceview_container import SourceViewContainer +from .editor_controller import EditorControllerMixin +from .editor_events import EditorEventsMixin -class EditorNotebook(Gtk.Notebook): +# NOTE: https://github.com/Axel-Erfurt/TextEdit/tree/b65f09be945196eb05bef83d81a6abcd129b4eb0 + +class EditorNotebook(EditorEventsMixin, EditorControllerMixin, Gtk.Notebook): def __init__(self): super(EditorNotebook, self).__init__() @@ -48,6 +52,8 @@ class EditorNotebook(Gtk.Notebook): event_system.subscribe("keyboard_next_tab", self._keyboard_next_tab) event_system.subscribe("keyboard_scale_up_text", self._keyboard_scale_up_text) event_system.subscribe("keyboard_scale_down_text", self._keyboard_scale_down_text) + event_system.subscribe("keyboard_save_file", self._keyboard_save_file) + event_system.subscribe("keyboard_save_file_as", self._keyboard_save_file_as) def _add_action_widgets(self): start_box = Gtk.Box() @@ -75,9 +81,6 @@ class EditorNotebook(Gtk.Notebook): def _load_widgets(self): self.create_view() - def _keyboard_create_tab(self, _gfile): - self.create_view(gfile=_gfile) - def create_view(self, widget = None, eve = None, gfile = None): container = SourceViewContainer(self.close_tab) @@ -109,75 +112,3 @@ class EditorNotebook(Gtk.Notebook): def _dbl_click_create_view(self, notebook, eve): if eve.type == Gdk.EventType.DOUBLE_BUTTON_PRESS and eve.button == 1: # l-click ... - - def _toggle_highlight_line(self): - self.action_controller("toggle_highlight_line") - - def _keyboard_close_tab(self): - self.action_controller("close_tab") - - def _keyboard_next_tab(self): - self.action_controller("keyboard_next_tab") - - def _keyboard_prev_tab(self): - self.action_controller("keyboard_prev_tab") - - def _keyboard_scale_up_text(self): - self.action_controller("scale_up_text") - - def _keyboard_scale_down_text(self): - self.action_controller("scale_down_text") - - def _text_search(self, widget = None, eve = None): - self.action_controller("do_text_search", widget.get_text()) - - def action_controller(self, action = "", query = ""): - page_num = self.get_current_page() - container = self.get_nth_page( page_num ) - source_view = container.get_source_view() - - if action == "do_text_search": - self.do_text_search(source_view, query) - if action == "set_buffer_language": - self.set_buffer_language(source_view, query) - if action == "set_buffer_style": - self.set_buffer_style(source_view, query) - if action == "toggle_highlight_line": - self.toggle_highlight_line(source_view) - if action == "scale_up_text": - self.scale_up_text(source_view) - if action == "scale_down_text": - self.scale_down_text(source_view) - if action == "close_tab": - self.close_tab(None, container, source_view) - if action == "keyboard_prev_tab": - self.keyboard_prev_tab(page_num) - if action == "keyboard_next_tab": - self.keyboard_next_tab(page_num) - - - def do_text_search(self, query = ""): - source_view.scale_down_text() - - def set_buffer_language(self, source_view, language = "python3"): - source_view.set_buffer_language(language) - - def set_buffer_style(self, source_view, style = "tango"): - source_view.set_buffer_style(style) - - def keyboard_prev_tab(self, page_num): - page_num = self.get_n_pages() - 1 if page_num == 0 else page_num - 1 - self.set_current_page(page_num) - - def keyboard_next_tab(self, page_num): - page_num = 0 if self.get_n_pages() - 1 == page_num else page_num + 1 - self.set_current_page(page_num) - - def scale_up_text(self, source_view): - source_view.scale_up_text() - - def scale_down_text(self, source_view): - source_view.scale_down_text() - - def toggle_highlight_line(self, source_view): - source_view.toggle_highlight_line() diff --git a/src/core/widgets/base/sourceview/__init__.py b/src/core/widgets/base/sourceview/__init__.py new file mode 100644 index 0000000..619f9a0 --- /dev/null +++ b/src/core/widgets/base/sourceview/__init__.py @@ -0,0 +1,3 @@ +""" + SourceView Module +""" diff --git a/src/core/widgets/base/source_view.py b/src/core/widgets/base/sourceview/source_view.py similarity index 54% rename from src/core/widgets/base/source_view.py rename to src/core/widgets/base/sourceview/source_view.py index 9177d2b..2ebd5f6 100644 --- a/src/core/widgets/base/source_view.py +++ b/src/core/widgets/base/sourceview/source_view.py @@ -11,10 +11,11 @@ from gi.repository import Gio from gi.repository import GtkSource # Application imports +from .source_view_events import SourceViewEventsMixin -class SourceView(GtkSource.View): +class SourceView(SourceViewEventsMixin, GtkSource.View): def __init__(self): super(SourceView, self).__init__() @@ -25,7 +26,9 @@ class SourceView(GtkSource.View): self._file_watcher = None self._is_changed = False - self._buffer = self.get_buffer() + self._current_file: Gio.File = None + self._file_loader = None + self._buffer = self.get_buffer() self._setup_styling() self._setup_signals() @@ -56,27 +59,19 @@ class SourceView(GtkSource.View): def _setup_signals(self): self.connect("drag-data-received", self._on_drag_data_received) self._buffer.connect("mark-set", self._on_cursor_move) + self._buffer.connect('changed', self._is_modified) # self.completion.add_provider(srcCompleteonSnippets) # self.completion.add_provider(srcCompleteonWords) def _subscribe_to_events(self): ... - def _load_widgets(self): ... - def _create_default_tag(self): - self._general_style_tag = self._buffer.create_tag('general_style') - self._general_style_tag.set_property('size', 100) - self._general_style_tag.set_property('scale', 100) - - def set_buffer_language(self, language = "python3"): - self._buffer.set_language( self._language_manager.get_language(language) ) - - def set_buffer_style(self, style = "tango"): - self._buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) ) - + def _is_modified(self, *args): + self._is_changed = True + self.update_cursor_position() def get_file_watcher(self): return self._file_watcher @@ -109,28 +104,6 @@ class SourceView(GtkSource.View): if eve_type in [ Gio.FileMonitorEvent.CHANGED ]: ... - - def toggle_highlight_line(self, widget = None, eve = None): - self.set_highlight_current_line( not self.get_highlight_current_line() ) - - def scale_up_text(self, scale_step = 10): - current_scale = self._general_style_tag.get_property('scale') - start_itr = self._buffer.get_start_iter() - end_itr = self._buffer.get_end_iter() - - self._general_style_tag.set_property('scale', current_scale + scale_step) - self._buffer.apply_tag(self._general_style_tag, start_itr, end_itr) - - def scale_down_text(self, scale_step = 10): - tag_table = self._buffer.get_tag_table() - start_itr = self._buffer.get_start_iter() - end_itr = self._buffer.get_end_iter() - tag = tag_table.lookup('general_style') - - tag.set_property('scale', tag.get_property('scale') - scale_step) - self._buffer.apply_tag(tag, start_itr, end_itr) - - def _set_up_dnd(self): URI_TARGET_TYPE = 80 uri_target = Gtk.TargetEntry.new('text/uri-list', Gtk.TargetFlags(0), URI_TARGET_TYPE) @@ -145,80 +118,61 @@ class SourceView(GtkSource.View): uris = data.get_text().split("\n") if self._is_changed: + # TODO: Impliment change detection and offer to save as new file + # Need to insure self._current_file gets set for further flow logic to work # self.maybe_saved() ... - gfile = Gio.File.new_for_uri(uris[0]) - self.open_file(gfile) + if not self._current_file: + gfile = Gio.File.new_for_uri(uris[0]) + self.open_file(gfile) + uris.pop(0) - uris.pop(0) for uri in uris: - gfile = Gio.File.new_for_uri(uri) + gfile = None + try: + gfile = Gio.File.new_for_uri(uri) + except Exception as e: + gfile = Gio.File.new_for_path(uri) + event_system.emit('create_view', (None, None, gfile,)) def open_file(self, gfile, *args): + self._current_file = gfile + + self.load_file_info(gfile) + self.load_file_async(gfile) + self.grab_focus() + + def load_file_info(self, gfile): info = gfile.query_info("standard::*", 0, cancellable=None) content_type = info.get_content_type() display_name = info.get_display_name() tab_widget = self.get_parent().get_tab_widget() - lm = self._language_manager.guess_language(None, content_type) + try: + lm = self._language_manager.guess_language(None, content_type) + self.set_buffer_language( lm.get_id() ) + except Exception as e: + ... + + logger.debug(f"Detected Content Type: {content_type}") tab_widget.set_tab_label(display_name) event_system.emit("set_bottom_labels", (gfile, info)) - logger.debug(f"Detected Content Type: {content_type}") - with open(gfile.get_path(), 'r') as f: - data = f.read() - self._buffer.set_text(data) - try: - self.set_buffer_language( lm.get_id() ) - except Exception as e: - ... + def load_file_async(self, gfile): + file = GtkSource.File() + file.set_location(gfile) + self._file_loader = GtkSource.FileLoader.new(self._buffer, file) - # self.current_file = myfile - # self.current_filename = myfile.rpartition("/")[2] - # self.current_folder = path.dirname(myfile) - f.close() - # self.headerbar.set_subtitle(myfile) - # self.status_label.set_text(f"'{myfile}' loaded") - # self.headerbar.set_title("TextEdit") - self.grab_focus() - # self.is_changed = False + def finish_load_callback(obj, res, user_data=None): + self._file_loader.load_finish(res) + self._is_changed = False - - - def _on_cursor_move(self, buf, cursor_iter, mark, user_data = None): - if mark != buf.get_insert(): - return - - self.update_cursor_position() - - - def update_cursor_position(self): - iter = self._buffer.get_iter_at_mark(self._buffer.get_insert()) - chars = iter.get_offset() - row = iter.get_line() + 1 - col = self.get_visual_column(iter) + 1 - - classes = self._buffer.get_context_classes_at_iter(iter) - classes_str = "" - - i = 0 - for c in classes: - if len(classes) != i + 1: - classes_str += c + ", " - else: - classes_str += c - - cursor_data = f"char: {chars}, line: {row}, column: {col}, classes: {classes_str}" - logger.debug(cursor_data) - event_system.emit("set_line_char_label", (f"{row}:{col}",)) - - - # https://github.com/ptomato/inform7-ide/blob/main/src/actions.c - def action_uncomment_selection(self): - ... - - def action_comment_out_selection(self): - pass + self._file_loader.load_async(io_priority=98, + cancellable=None, + progress_callback=None, + progress_callback_data=None, + callback=finish_load_callback, + user_data=(None)) diff --git a/src/core/widgets/base/sourceview/source_view_events.py b/src/core/widgets/base/sourceview/source_view_events.py new file mode 100644 index 0000000..13f3cec --- /dev/null +++ b/src/core/widgets/base/sourceview/source_view_events.py @@ -0,0 +1,73 @@ +# Python imports + +# Lib imports + +# Application imports + + + +class SourceViewEventsMixin: + def _create_default_tag(self): + self._general_style_tag = self._buffer.create_tag('general_style') + self._general_style_tag.set_property('size', 100) + self._general_style_tag.set_property('scale', 100) + + def set_buffer_language(self, language = "python3"): + self._buffer.set_language( self._language_manager.get_language(language) ) + + def set_buffer_style(self, style = "tango"): + self._buffer.set_style_scheme( self._style_scheme_manager.get_scheme(style) ) + + def toggle_highlight_line(self, widget = None, eve = None): + self.set_highlight_current_line( not self.get_highlight_current_line() ) + + def scale_up_text(self, scale_step = 10): + current_scale = self._general_style_tag.get_property('scale') + start_itr = self._buffer.get_start_iter() + end_itr = self._buffer.get_end_iter() + + self._general_style_tag.set_property('scale', current_scale + scale_step) + self._buffer.apply_tag(self._general_style_tag, start_itr, end_itr) + + def scale_down_text(self, scale_step = 10): + tag_table = self._buffer.get_tag_table() + start_itr = self._buffer.get_start_iter() + end_itr = self._buffer.get_end_iter() + tag = tag_table.lookup('general_style') + + tag.set_property('scale', tag.get_property('scale') - scale_step) + self._buffer.apply_tag(tag, start_itr, end_itr) + + def _on_cursor_move(self, buf, cursor_iter, mark, user_data = None): + if mark != buf.get_insert(): + return + + self.update_cursor_position() + + def update_cursor_position(self): + iter = self._buffer.get_iter_at_mark(self._buffer.get_insert()) + chars = iter.get_offset() + row = iter.get_line() + 1 + col = self.get_visual_column(iter) + 1 + + classes = self._buffer.get_context_classes_at_iter(iter) + classes_str = "" + + i = 0 + for c in classes: + if len(classes) != i + 1: + classes_str += c + ", " + else: + classes_str += c + + cursor_data = f"char: {chars}, line: {row}, column: {col}, classes: {classes_str}" + logger.debug(cursor_data) + event_system.emit("set_line_char_label", (f"{row}:{col}",)) + + + # https://github.com/ptomato/inform7-ide/blob/main/src/actions.c + def action_uncomment_selection(self): + ... + + def action_comment_out_selection(self): + pass diff --git a/src/core/widgets/base/sourceview_container.py b/src/core/widgets/base/sourceview_container.py index ee9e2c3..500ed6f 100644 --- a/src/core/widgets/base/sourceview_container.py +++ b/src/core/widgets/base/sourceview_container.py @@ -7,12 +7,10 @@ from gi.repository import Gtk # Application imports from ..tab_header_widget import TabHeaderWidget -from .source_view import SourceView +from .sourceview.source_view import SourceView -# NOTE: https://github.com/Axel-Erfurt/TextEdit/tree/b65f09be945196eb05bef83d81a6abcd129b4eb0 - class SourceViewContainer(Gtk.ScrolledWindow): def __init__(self, close_tab): super(SourceViewContainer, self).__init__() diff --git a/user_config/usr/share/newton_editor/key-bindings.json b/user_config/usr/share/newton_editor/key-bindings.json index 2cf294d..2dea9c7 100644 --- a/user_config/usr/share/newton_editor/key-bindings.json +++ b/user_config/usr/share/newton_editor/key-bindings.json @@ -8,6 +8,8 @@ "open_files" : "o", "keyboard_create_tab" : "t", "keyboard_close_tab" : "w", + "keyboard_save_file" : "s", + "keyboard_save_file_as" : "s", "keyboard_up" : "Up", "keyboard_down" : "Down", "keyboard_left" : "Left",