From bc23fa9b0b101401c710032dc212ef97a64bb23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gon=C3=A7alo=20Silva?= Date: Mon, 22 Apr 2019 01:31:32 +0100 Subject: [PATCH] Make characters-per-line configurable This is in preparation for the side-by-side preview, where the editor needs to become more adaptable. It indirectly fixes #141, as users can now change the desired line-length, although there is no UI setting for it. --- data/de.wolfvollprecht.UberWriter.gschema.xml | 7 +++ data/media/css/gtk/base.css | 16 +++++- uberwriter/text_view.py | 57 +++++++++++++++++-- uberwriter/window.py | 50 +--------------- 4 files changed, 74 insertions(+), 56 deletions(-) diff --git a/data/de.wolfvollprecht.UberWriter.gschema.xml b/data/de.wolfvollprecht.UberWriter.gschema.xml index e7d4bfc..eccc8bf 100644 --- a/data/de.wolfvollprecht.UberWriter.gschema.xml +++ b/data/de.wolfvollprecht.UberWriter.gschema.xml @@ -69,6 +69,13 @@ Which statistic is shown on the main window. + + 66 + Characters per line + + Maximum number of characters per line within the editor. + + diff --git a/data/media/css/gtk/base.css b/data/media/css/gtk/base.css index ff0736d..f1707dc 100644 --- a/data/media/css/gtk/base.css +++ b/data/media/css/gtk/base.css @@ -33,11 +33,23 @@ font-size: 16px; } -.uberwriter-window.small .uberwriter-editor { +.uberwriter-window .uberwriter-editor.size14 { font-size: 14px; } -.uberwriter-window.large .uberwriter-editor { +.uberwriter-window .uberwriter-editor.size15 { + font-size: 15px; +} + +.uberwriter-window .uberwriter-editor.size16 { + font-size: 16px; +} + +.uberwriter-window .uberwriter-editor.size17 { + font-size: 17px; +} + +.uberwriter-window .uberwriter-editor.size18 { font-size: 18px; } diff --git a/uberwriter/text_view.py b/uberwriter/text_view.py index e70d8e8..f30e4ab 100644 --- a/uberwriter/text_view.py +++ b/uberwriter/text_view.py @@ -39,7 +39,9 @@ class TextView(Gtk.TextView): 'redo': (GObject.SignalFlags.ACTION, None, ()) } - def __init__(self): + font_sizes = [18, 17, 16, 15, 14] # Must match CSS selectors in gtk/base.css + + def __init__(self, line_chars): super().__init__() # Appearance @@ -49,9 +51,15 @@ class TextView(Gtk.TextView): self.set_pixels_inside_wrap(8) self.get_style_context().add_class('uberwriter-editor') + # Text sizing + self.props.halign = Gtk.Align.FILL + self.font_size = 16 + self.line_chars = line_chars + self.get_style_context().add_class('size16') + # General behavior - self.get_buffer().connect('changed', self.on_text_changed) self.connect('size-allocate', self.on_size_allocate) + self.get_buffer().connect('changed', self.on_text_changed) # Spell checking self.gspell_view = Gspell.TextView.get_from_gtk_text_view(self) @@ -113,18 +121,22 @@ class TextView(Gtk.TextView): if self.scroller: self.scroller.set_scroll_scale(scale) + def on_size_allocate(self, *_): + self.update_horizontal_margin() + self.update_vertical_margin() + self.markup.update_margins_indents() + def on_text_changed(self, *_): self.markup.apply() self.smooth_scroll_to() - def on_size_allocate(self, *_): - self.update_vertical_margin() - self.markup.update_margins_indents() - def on_parent_set(self, *_): parent = self.get_parent() if parent: self.scroller = TextViewScroller(self, parent) + # Request a size that fits the minimum font size comfortably. + parent.set_size_request( + self.pad_chars(self.font_sizes[-1]) * self.font_width(self.font_sizes[-1]), 500) else: self.scroller = None @@ -153,6 +165,26 @@ class TextView(Gtk.TextView): self.markup.apply() self.smooth_scroll_to() + def update_horizontal_margin(self): + width = self.get_allocation().width + + # Ensure the appropriate font size is being used + for size in self.font_sizes: + min_width = (self.line_chars + self.pad_chars(size) + 1) * self.font_width(size) - 1 + if width >= min_width: + if size != self.font_size: + self.font_size = size + for s in self.font_sizes: + self.get_style_context().remove_class("size{}".format(s)) + self.get_style_context().add_class("size{}".format(size)) + break + + # Apply margin with the remaining space to allow for markup + line_width = (self.line_chars + 1) * int(self.font_width(self.font_size)) - 1 + horizontal_margin = (width - line_width) / 2 + self.props.left_margin = horizontal_margin + self.props.right_margin = horizontal_margin + def update_vertical_margin(self): if self.focus_mode: height = self.get_allocation().height @@ -191,3 +223,16 @@ class TextView(Gtk.TextView): if mark is None: mark = self.get_buffer().get_insert() GLib.idle_add(self.scroller.smooth_scroll_to_mark, mark, self.focus_mode) + + def pad_chars(self, font_size): + """Returns the amount of character padding for font_size. + + Markup can use up to 6 in normal conditions.""" + + return 8 * (1 + font_size - self.font_sizes[-1]) + + @staticmethod + def font_width(font_size): + """Returns the font width for a given size. Specific to Fira Mono.""" + + return font_size * 1 / 1.6 diff --git a/uberwriter/window.py b/uberwriter/window.py index 5ef61de..c9e62a2 100644 --- a/uberwriter/window.py +++ b/uberwriter/window.py @@ -77,9 +77,10 @@ class Window(Gtk.ApplicationWindow): self.builder = get_builder('Window') root = self.builder.get_object("FullscreenOverlay") root.connect('style-updated', self.apply_current_theme) + self.connect("delete-event", self.on_delete_called) self.add(root) - self.set_default_size(900, 500) + self.set_default_size(1000, 600) # Preferences self.settings = Settings.new() @@ -155,11 +156,6 @@ class Window(Gtk.ApplicationWindow): ### self.searchreplace = SearchAndReplace(self, self.text_view) - # Window resize - self.window_resize(self) - self.connect("configure-event", self.window_resize) - self.connect("delete-event", self.on_delete_called) - # Set current theme self.apply_current_theme() self.get_style_context().add_class('uberwriter-window') @@ -233,48 +229,6 @@ class Window(Gtk.ApplicationWindow): self.text_view.set_hemingway_mode(state.get_boolean()) self.text_view.grab_focus() - def window_resize(self, window, event=None): - """set paddings dependant of the window size - """ - - # Ensure the window receiving the event is the one we care about, ie. the main window. - # On Wayland (bug?), sub-windows such as the recents popover will also trigger this. - if event and event.window != window.get_window(): - return - - # Adjust text editor width depending on window width, so that: - # - The number of characters per line is adequate (http://webtypography.net/2.1.2) - # - The number of characters stays constant while resizing the window / font - # - There is enough text margin for MarkupBuffer to apply indents / negative margins - # - # TODO: Avoid hard-coding. Font size is clearer than unclear dimensions, but not ideal. - w_width = event.width if event else window.get_allocation().width - if w_width < 900: - font_size = 14 - self.get_style_context().add_class("small") - self.get_style_context().remove_class("large") - - elif w_width < 1280: - font_size = 16 - self.get_style_context().remove_class("small") - self.get_style_context().remove_class("large") - - else: - font_size = 18 - self.get_style_context().remove_class("small") - self.get_style_context().add_class("large") - - font_width = int(font_size * 1/1.6) # Ratio specific to Fira Mono - width = 67 * font_width - 1 # 66 characters - horizontal_margin = 8 * font_width # 8 characters - width_request = width + horizontal_margin * 2 - - if self.text_view.props.width_request != width_request: - self.text_view.props.width_request = width_request - self.text_view.set_left_margin(horizontal_margin) - self.text_view.set_right_margin(horizontal_margin) - self.scrolled_window.props.width_request = width_request - # TODO: refactorizable def save_document(self, _widget=None, _data=None): """provide to the user a filechooser and save the document