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.
github/fork/yochananmarqos/patch-1
Gonçalo Silva 2019-04-22 01:31:32 +01:00
parent 562cc7e200
commit bc23fa9b0b
4 changed files with 74 additions and 56 deletions

View File

@ -69,6 +69,13 @@
Which statistic is shown on the main window.
</description>
</key>
<key name='characters-per-line' type='i'>
<default>66</default>
<summary>Characters per line</summary>
<description>
Maximum number of characters per line within the editor.
</description>
</key>
</schema>

View File

@ -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;
}

View File

@ -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

View File

@ -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