diff --git a/data/ui/Window.ui b/data/ui/Window.ui index 16fd925..466d1b3 100644 --- a/data/ui/Window.ui +++ b/data/ui/Window.ui @@ -417,14 +417,7 @@ False start - - True - False - start - - - - + diff --git a/uberwriter/headerbars.py b/uberwriter/headerbars.py index 19991ae..ba81346 100644 --- a/uberwriter/headerbars.py +++ b/uberwriter/headerbars.py @@ -16,46 +16,121 @@ """Manage all the headerbars related stuff """ -from collections import namedtuple -from gettext import gettext as _ - import gi gi.require_version('Gtk', '3.0') -from gi.repository import Gtk +from gi.repository import Gtk, GLib from uberwriter.helpers import get_descendant -class MainHeaderbar: #pylint: disable=too-few-public-methods +class BaseHeaderbar: + """Base class for all headerbars + """ + def __init__(self, app): + + self.builder = Gtk.Builder() + self.builder.add_from_resource( + "/de/wolfvollprecht/UberWriter/ui/Headerbar.ui") + + self.hb = self.builder.get_object("Headerbar") + self.hb_revealer = self.builder.get_object("titlebar_revealer") + + self.menu_button = self.builder.get_object("menu_button") + self.recents_button = self.builder.get_object("recents_button") + + +class MainHeaderbar(BaseHeaderbar): # pylint: disable=too-few-public-methods """Sets up the main application headerbar """ def __init__(self, app): - builder = Gtk.Builder() - builder.add_from_resource( - "/de/wolfvollprecht/UberWriter/ui/Headerbar.ui") + BaseHeaderbar.__init__(self, app) - self.hb = builder.get_object("Headerbar") - - self.menu_button = builder.get_object("menu_button") - self.recents_button = builder.get_object("recents_button") + self.hb.set_show_close_button(True) add_menus(self, app) - self.hb_revealer = Gtk.Revealer(name='titlebar-revealer') - self.hb_revealer.add(self.hb) - self.hb_revealer.props.transition_duration = 750 - self.hb_revealer.props.transition_type = Gtk.RevealerTransitionType.CROSSFADE - self.hb_revealer.show() + self.hb_revealer.props.transition_duration = 0 + + +class FullscreenHeaderbar(BaseHeaderbar): + """Sets up and manages the fullscreen headerbar and his events + """ + + def __init__(self, fs_builder, app): + + BaseHeaderbar.__init__(self, app) + + self.hb.set_show_close_button(False) + + self.exit_fs_button = self.builder.get_object("exit_fs_button") + self.exit_fs_button.set_visible(True) + + add_menus(self, app) + + self.events = fs_builder.get_object("FullscreenEventbox") + self.events.add(self.hb_revealer) + + # this is a little tricky + # we show hb when the cursor enters an area of 1px at the top + # as the hb is shown the height of the eventbox grows to accomodate it + self.events.connect('enter_notify_event', self.show_fs_hb) + self.events.connect('leave_notify_event', self.hide_fs_hb) + self.menu_button.get_popover().connect('closed', self.hide_fs_hb) + + def show_fs_hb(self, _widget, _data=None): + """show headerbar of the fullscreen mode + """ self.hb_revealer.set_reveal_child(True) - self.hb_container = Gtk.Frame(name='titlebar-container') - self.hb_container.set_shadow_type(Gtk.ShadowType.NONE) - self.hb_container.add(self.hb_revealer) - self.hb_container.show() + def hide_fs_hb(self, _widget, _data=None): + """hide headerbar of the fullscreen mode + """ + if self.menu_button.get_active(): + pass + else: + self.hb_revealer.set_reveal_child(False) - self.hb.show_all() + +class DummyHeaderbar(BaseHeaderbar): + """Sets up and manages the dummy headerbar wich fades away when entering + the free-distracting mode + """ + + def __init__(self, app): + + BaseHeaderbar.__init__(self, app) + + self.hb.set_show_close_button(True) + self.hb_revealer.set_transition_type( + Gtk.RevealerTransitionType.CROSSFADE) + self.hb_revealer.set_reveal_child(False) + + self.menu_button.set_sensitive(True) + self.recents_button.set_sensitive(True) + + def show_dm_hb(self): + """show dummy headerbar: + It appears instantly to inmediatly fade away + """ + self.hb_revealer.set_transition_duration(0) + self.hb_revealer.set_reveal_child(True) + self.hb_revealer.set_transition_duration(600) + self.hb_revealer.set_reveal_child(False) + + def hide_dm_hb(self): + """hide dummy headerbar + It appears slowly to inmediatly dissapear + """ + self.hb_revealer.set_transition_duration(400) + self.hb_revealer.set_reveal_child(True) + GLib.timeout_add(400, self.hide_dm_hb_with_wait) + + def hide_dm_hb_with_wait(self): + self.hb_revealer.set_transition_duration(0) + self.hb_revealer.set_reveal_child(False) + return False class PreviewHeaderbar: @@ -67,10 +142,11 @@ class PreviewHeaderbar: self.hb.props.show_close_button = True self.hb.get_style_context().add_class("titlebar") - self.hb_revealer = Gtk.Revealer(name="titlebar-revealer") + self.hb_revealer = Gtk.Revealer(name="titlebar-revealer-pv") self.hb_revealer.add(self.hb) self.hb_revealer.props.transition_duration = 750 - self.hb_revealer.props.transition_type = Gtk.RevealerTransitionType.CROSSFADE + self.hb_revealer.set_transition_type( + Gtk.RevealerTransitionType.CROSSFADE) self.hb_revealer.show() self.hb_revealer.set_reveal_child(True) @@ -82,55 +158,9 @@ class PreviewHeaderbar: self.hb.show_all() -class FullscreenHeaderbar: - """Sets up and manages the fullscreen headerbar and his events - """ - - def __init__(self, fs_builder, app): - - builder = Gtk.Builder() - builder.add_from_resource( - "/de/wolfvollprecht/UberWriter/ui/Headerbar.ui") - - self.hb = builder.get_object("Headerbar") - self.hb.set_show_close_button(False) - self.hb.show_all() - - self.menu_button = builder.get_object("menu_button") - self.recents_button = builder.get_object("recents_button") - - self.exit_fs_button = builder.get_object("exit_fs_button") - self.exit_fs_button.set_visible(True) - - add_menus(self, app) - - self.events = fs_builder.get_object("FullscreenEventbox") - self.revealer = fs_builder.get_object( - "FullscreenHbPlaceholder") - self.revealer.add(self.hb) - - # this is a little tricky - # we show hb when the cursor enters an area of 1 px at the top of the window - # as the hb is shown the height of the eventbox grows to accomodate it - self.events.connect('enter_notify_event', self.show_fs_hb) - self.events.connect('leave_notify_event', self.hide_fs_hb) - self.menu_button.get_popover().connect('closed', self.hide_fs_hb) - - def show_fs_hb(self, _widget, _data=None): - """show headerbar of the fullscreen mode - """ - self.revealer.set_reveal_child(True) - - def hide_fs_hb(self, _widget, _data=None): - """hide headerbar of the fullscreen mode - """ - if self.menu_button.get_active(): - pass - else: - self.revealer.set_reveal_child(False) - - def add_menus(headerbar, app): + """ Add menu models to hb buttons + """ # Add menu model to the menu button diff --git a/uberwriter/main_window.py b/uberwriter/main_window.py index 5ac71f1..55646f6 100644 --- a/uberwriter/main_window.py +++ b/uberwriter/main_window.py @@ -84,10 +84,21 @@ class MainWindow(StyledWindow): self.settings = Settings.new() # Headerbars + self.last_height = 0 self.headerbar = headerbars.MainHeaderbar(app) - self.set_titlebar(self.headerbar.hb_container) + self.headerbar.hb_revealer.connect( + "size_allocate", self.header_size_allocate) + self.set_titlebar(self.headerbar.hb_revealer) + self.fs_headerbar = headerbars.FullscreenHeaderbar(builder, app) + # The dummy headerbar is a cosmetic hack to be able to + # crossfade the hb on top of the window + self.dm_headerbar = headerbars.DummyHeaderbar(app) + root.add_overlay(self.dm_headerbar.hb_revealer) + root.reorder_overlay(self.dm_headerbar.hb_revealer, 0) + root.set_overlay_pass_through(self.dm_headerbar.hb_revealer, True) + self.title_end = " – UberWriter" self.set_headerbar_title("New File" + self.title_end) @@ -103,6 +114,7 @@ class MainWindow(StyledWindow): # Setup text editor self.text_view = TextView(self.settings.get_int("characters-per-line")) + self.text_view.set_top_margin(80) self.text_view.connect('focus-out-event', self.focus_out) self.text_view.get_buffer().connect('changed', self.on_text_changed) self.text_view.show() @@ -151,6 +163,24 @@ class MainWindow(StyledWindow): ### self.searchreplace = SearchAndReplace(self, self.text_view, builder) + def header_size_allocate(self, widget, allocation): + """ When the main hb starts to shrink its size, add that size + to the textview margin, so it stays in place + """ + + # prevent 1px jumps + if allocation.height == 1 and not widget.get_child_revealed(): + allocation.height = 0 + + height = self.headerbar.hb.get_allocated_height() - allocation.height + if height == self.last_height: + return + + self.last_height = height + + self.text_view.update_vertical_margin(height) + self.text_view.queue_draw() + def on_text_changed(self, *_args): """called when the text changes, sets the self.did_change to true and updates the title and the counters to reflect that @@ -183,7 +213,7 @@ class MainWindow(StyledWindow): """toggle focusmode """ - self.text_view.set_focus_mode(state.get_boolean()) + self.text_view.set_focus_mode(state.get_boolean(), self.headerbar.hb.get_allocated_height()) self.text_view.grab_focus() def set_hemingway_mode(self, state): @@ -519,7 +549,6 @@ class MainWindow(StyledWindow): def open_recent(self, _widget, data=None): """open the given recent document """ - print("open") if data: if self.check_change() == Gtk.ResponseType.CANCEL: @@ -563,8 +592,30 @@ class MainWindow(StyledWindow): self.reveal_top_bottom_bars(True) def reveal_top_bottom_bars(self, reveal): + """handles (in conjunction with header_size_allocate) + the fading in and out of the headerbar + """ + # The logic may seem confusing because similar things are + # handled in headerbars.py, but for convenience (adding classes + # to the main window, and delayed calls) some functions are split + # between here and there + + # TODO: rework this logic? + + def reveal_hb(): + self.headerbar.hb_revealer.set_reveal_child(True) + self.get_style_context().remove_class("focus") + return False + if self.top_bottom_bars_visible != reveal: - self.headerbar.hb_revealer.set_reveal_child(reveal) + if reveal: + self.dm_headerbar.hide_dm_hb() + GLib.timeout_add(400, reveal_hb) + else: + self.headerbar.hb_revealer.set_reveal_child(False) + self.dm_headerbar.show_dm_hb() + self.get_style_context().add_class("focus") + self.stats_revealer.set_reveal_child(reveal) for revealer in self.preview_handler.get_top_bottom_bar_revealers(): revealer.set_reveal_child(reveal) diff --git a/uberwriter/text_view.py b/uberwriter/text_view.py index 1c4e9bc..87cabce 100644 --- a/uberwriter/text_view.py +++ b/uberwriter/text_view.py @@ -54,6 +54,9 @@ class TextView(Gtk.TextView): self.set_pixels_inside_wrap(8) self.get_style_context().add_class('uberwriter-editor') + self.set_margin_left(8) + self.set_margin_right(8) + # Text sizing self.props.halign = Gtk.Align.FILL self.line_chars = line_chars @@ -144,7 +147,6 @@ class TextView(Gtk.TextView): def on_size_allocate(self, *_): self.update_horizontal_margin() - self.update_vertical_margin() self.markup.update_margins_indents() self.queue_draw() @@ -192,14 +194,14 @@ class TextView(Gtk.TextView): self.frozen_scroll_scale = None self.queue_draw() - def set_focus_mode(self, focus_mode): + def set_focus_mode(self, focus_mode, hb_height): """Toggle focus mode. When in focus mode, the cursor sits in the middle of the text view, and the surrounding text is greyed out.""" self.focus_mode = focus_mode - self.update_vertical_margin() + self.update_vertical_margin(hb_size=hb_height) self.markup.apply() self.smooth_scroll_to() self.set_spellcheck(self.spellcheck) @@ -227,13 +229,14 @@ class TextView(Gtk.TextView): self.props.left_margin = horizontal_margin self.props.right_margin = horizontal_margin - def update_vertical_margin(self): + def update_vertical_margin(self, top_margin=0, hb_size=0): if self.focus_mode: - height = self.get_allocation().height - self.props.top_margin = height / 2 - self.props.bottom_margin = height / 2 + height = self.get_allocation().height + top_margin + hb_size + + self.props.top_margin = height / 2 + top_margin + self.props.bottom_margin = height / 2 - top_margin else: - self.props.top_margin = 80 + self.props.top_margin = 80 + top_margin self.props.bottom_margin = 64 def set_hemingway_mode(self, hemingway_mode):