From 0e2f731ff48a3a558f854eeb06314684dfa8fb6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Mon, 11 Nov 2019 01:27:39 +0100 Subject: [PATCH 01/13] Use a .ui file to define the headerbar This makes all the headerbar code more readable and easier to tweak --- data/uberwriter.gresource.xml | 1 + data/ui/Headerbar.ui | 132 ++++++++++++++++++++++++++++++++++ data/ui/Recents.ui | 5 +- data/ui/Window.ui | 10 +-- uberwriter/headerbars.py | 122 ++++++++++--------------------- 5 files changed, 177 insertions(+), 93 deletions(-) create mode 100644 data/ui/Headerbar.ui diff --git a/data/uberwriter.gresource.xml b/data/uberwriter.gresource.xml index ea9446b..c85d3be 100644 --- a/data/uberwriter.gresource.xml +++ b/data/uberwriter.gresource.xml @@ -9,6 +9,7 @@ ui/Recents.ui ui/Shortcuts.ui ui/Window.ui + ui/Headerbar.ui About.ui diff --git a/data/ui/Headerbar.ui b/data/ui/Headerbar.ui new file mode 100644 index 0000000..61e4843 --- /dev/null +++ b/data/ui/Headerbar.ui @@ -0,0 +1,132 @@ + + + + + + True + False + view-restore-symbolic + + + True + False + system-search-symbolic + + + True + False + True + + + New + True + True + True + app.new + + + + + True + False + + + Open + True + True + True + app.open + + + False + True + 0 + + + + + True + True + True + Open Recent + app.on_open_recent + + + + + + False + True + 1 + + + + + + 1 + + + + + True + True + True + app.fullscreen + exit_fs_icon + + + end + 1 + + + + + True + True + True + + + True + False + Menu + open-menu-symbolic + + + + + end + 2 + + + + + True + True + True + Find + app.search + search_icon + True + + + end + 3 + + + + + Save + True + True + True + app.save + + + end + 4 + + + + diff --git a/data/ui/Recents.ui b/data/ui/Recents.ui index 06bc23a..a7b237c 100644 --- a/data/ui/Recents.ui +++ b/data/ui/Recents.ui @@ -17,7 +17,10 @@ True False start - 6 + 6 + 6 + 6 + 6 recent_md_filter 20 False diff --git a/data/ui/Window.ui b/data/ui/Window.ui index 5d17ee0..16fd925 100644 --- a/data/ui/Window.ui +++ b/data/ui/Window.ui @@ -414,7 +414,6 @@ 1 - True False start @@ -423,14 +422,7 @@ False start - - True - False - start - - - - + diff --git a/uberwriter/headerbars.py b/uberwriter/headerbars.py index a70af47..19991ae 100644 --- a/uberwriter/headerbars.py +++ b/uberwriter/headerbars.py @@ -31,9 +31,17 @@ class MainHeaderbar: #pylint: disable=too-few-public-methods """ def __init__(self, app): - self.hb = Gtk.HeaderBar().new() #pylint: disable=C0103 - self.hb.props.show_close_button = True - self.hb.get_style_context().add_class("titlebar") + + builder = Gtk.Builder() + builder.add_from_resource( + "/de/wolfvollprecht/UberWriter/ui/Headerbar.ui") + + self.hb = builder.get_object("Headerbar") + + self.menu_button = builder.get_object("menu_button") + self.recents_button = builder.get_object("recents_button") + + add_menus(self, app) self.hb_revealer = Gtk.Revealer(name='titlebar-revealer') self.hb_revealer.add(self.hb) @@ -47,9 +55,6 @@ class MainHeaderbar: #pylint: disable=too-few-public-methods self.hb_container.add(self.hb_revealer) self.hb_container.show() - self.btns = main_buttons(app) - pack_main_buttons(self.hb, self.btns) - self.hb.show_all() @@ -81,35 +86,35 @@ class FullscreenHeaderbar: """Sets up and manages the fullscreen headerbar and his events """ - def __init__(self, builder, app): - self.events = builder.get_object("FullscreenEventbox") - self.revealer = builder.get_object( - "FullscreenHbPlaceholder") - self.revealer.show() - self.revealer.set_reveal_child(False) + def __init__(self, fs_builder, app): - self.hb = builder.get_object("FullscreenHeaderbar") #pylint: disable=C0103 - self.hb.get_style_context().add_class("titlebar") - self.hb.show() - self.events.hide() - - self.btns = main_buttons(app) - - fs_btn_exit = Gtk.Button().new_from_icon_name("view-restore-symbolic", - Gtk.IconSize.BUTTON) - fs_btn_exit.set_tooltip_text(_("Exit Fullscreen")) - fs_btn_exit.set_action_name("app.fullscreen") - - pack_main_buttons(self.hb, self.btns, fs_btn_exit) + 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.btns.menu.get_popover().connect('closed', 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 @@ -119,34 +124,24 @@ class FullscreenHeaderbar: def hide_fs_hb(self, _widget, _data=None): """hide headerbar of the fullscreen mode """ - if self.btns.menu.get_active(): + if self.menu_button.get_active(): pass else: self.revealer.set_reveal_child(False) -def main_buttons(app): - """constructor for the headerbar buttons +def add_menus(headerbar, app): - Returns: - [NamedTupple] -- tupple of Gtk.Buttons - """ - - Button = namedtuple("Button", "new open_recent save search menu") - btn = Button(Gtk.Button().new_with_label(_("New")), - Gtk.Box().new(0, 0), - Gtk.Button().new_with_label(_("Save")), - Gtk.Button().new_from_icon_name("system-search-symbolic", - Gtk.IconSize.BUTTON), - Gtk.MenuButton().new()) + # Add menu model to the menu button builder_window_menu = Gtk.Builder() builder_window_menu.add_from_resource( "/de/wolfvollprecht/UberWriter/ui/Menu.ui") model = builder_window_menu.get_object("Menu") - open_button = Gtk.Button().new_with_label(_("Open")) - open_button.set_action_name("app.open") + headerbar.menu_button.set_menu_model(model) + + # Add recents menu to the open recents button recents_builder = Gtk.Builder() recents_builder.add_from_resource( @@ -159,44 +154,5 @@ def main_buttons(app): recents_wd = recents_builder.get_object("recent_md_widget") recents_wd.connect('item-activated', app.on_open_recent) - recents_button = Gtk.MenuButton().new() - recents_button.set_image(Gtk.Image.new_from_icon_name("pan-down-symbolic", - Gtk.IconSize.BUTTON)) - recents_button.set_tooltip_text(_("Open Recent")) - recents_button.set_popover(recents) - - btn.open_recent.get_style_context().add_class("linked") - btn.open_recent.pack_start(open_button, False, False, 0) - btn.open_recent.pack_end(recents_button, False, False, 0) - - btn.search.set_tooltip_text(_("Find")) - btn.menu.set_tooltip_text(_("Menu")) - btn.menu.set_image(Gtk.Image.new_from_icon_name("open-menu-symbolic", - Gtk.IconSize.BUTTON)) - btn.menu.set_use_popover(True) - btn.menu.set_menu_model(model) - btn.new.set_action_name("app.new") - btn.save.set_action_name("app.save") - btn.search.set_action_name("app.search") - - return btn - - -def pack_main_buttons(headerbar, btn, btn_exit=None): - """Pack the given buttons in the given headerbar - - Arguments: - headerbar {Gtk.HeaderBar} -- The headerbar where to put the buttons - btn {Tupple of Gtk.Buttons} -- The buttons to pack - - Keyword Arguments: - btn_exit {Gtk.Button} -- Optional exit fullscreen button (default: {None}) - """ - - headerbar.pack_start(btn.new) - headerbar.pack_start(btn.open_recent) - if btn_exit: - headerbar.pack_end(btn_exit) - headerbar.pack_end(btn.menu) - headerbar.pack_end(btn.search) - headerbar.pack_end(btn.save) + headerbar.recents_button.set_popover(recents) + headerbar.recents_button.set_sensitive(True) From f091e5ac1a466a6fa5d1a09ebf68638bf01c3219 Mon Sep 17 00:00:00 2001 From: Manuel Genoves Date: Thu, 14 Nov 2019 12:45:54 +0100 Subject: [PATCH 02/13] update gitignore --- .gitignore | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index 9300151..8be02eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,26 +1,8 @@ -build/lib.linux-x86_64-2.7 *.pyc __pycache__/ build/ -_build/ -debian/uberwriter/DEBIAN -debian/uberwriter/opt -debian/uberwriter/usr -flatpak/* -!flatpak/uberwriter.json -!flatpak/de.wolfvollprecht.UberWriter.* -!flatpak/flatpak_texlive.json -!flatpak/texlive_install.sh -!flatpak/python3-enchant.json -!flatpak/python3-packages.json -*.py~ -data/ui/shortcut_handlers -*.ui~ +*.*~ .vscode/ .idea/ -*.glade~ -dist/uberwriter-2.0b0-py3.7.egg builddir/* -dist/ -uberwriter.egg-info -build-aux/flatpak/.flatpak-builder/* \ No newline at end of file +build-aux/* \ No newline at end of file From b5260e3906b03784e451556d1b7a8ae9987a68ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Sat, 23 Nov 2019 02:08:47 +0100 Subject: [PATCH 03/13] add curved corners for focus mode --- data/media/css/gtk/base.css | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/data/media/css/gtk/base.css b/data/media/css/gtk/base.css index 810bc9c..cc3176f 100644 --- a/data/media/css/gtk/base.css +++ b/data/media/css/gtk/base.css @@ -27,6 +27,19 @@ caret-color: @theme_fg_color; } +.uberwriter-window.focus:not(.tiled):not(.tiled-top):not(.tiled-bottom):not(.tiled-left):not(.tiled-right):not(.maximized):not(.fullscreen) { + border-top-left-radius: 8px; + border-top-right-radius: 8px; +} + +.uberwriter-window.focus:not(.tiled):not(.tiled-top):not(.tiled-bottom):not(.tiled-left):not(.tiled-right):not(.maximized):not(.fullscreen):dir(ltr) scrollbar { + border-top-right-radius: 8px; +} + +.uberwriter-window.focus:not(.tiled):not(.tiled-top):not(.tiled-bottom):not(.tiled-left):not(.tiled-right):not(.maximized):not(.fullscreen):dir(rtl) scrollbar { + border-top-left-radius: 8px; +} + #titlebar-revealer { padding: 0; } From f766c3703d70c1f9218c64ae19e85c803ea5bae8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Sat, 23 Nov 2019 02:09:22 +0100 Subject: [PATCH 04/13] add revealer to the headerbar default class --- data/ui/Headerbar.ui | 191 +++++++++++++++++++++++-------------------- 1 file changed, 101 insertions(+), 90 deletions(-) diff --git a/data/ui/Headerbar.ui b/data/ui/Headerbar.ui index 61e4843..0a2f6aa 100644 --- a/data/ui/Headerbar.ui +++ b/data/ui/Headerbar.ui @@ -12,121 +12,132 @@ False system-search-symbolic - + + titlebar-revealer True False - True + start + slide-up + 750 + True + - - New - True - True - True - app.new - - - - + True False - Open + New True True True - app.open + app.new - - False - True - 0 - - + True - True - True - Open Recent - app.on_open_recent + False - + + Open + True + True + True + app.open + + + False + True + 0 + + + + True + True + True + Open Recent + app.on_open_recent + + + + + + False + True + 1 + + + - False - True 1 - - - - 1 - - - - - True - True - True - app.fullscreen - exit_fs_icon - - - end - 1 - - - - - True - True - True - - True - False - Menu - open-menu-symbolic + + True + True + True + app.fullscreen + exit_fs_icon + + end + 1 + + + + + True + True + True + + + True + False + Menu + open-menu-symbolic + + + + + end + 2 + + + + + True + True + True + Find + app.search + search_icon + True + + + end + 3 + + + + + Save + True + True + True + app.save + + + end + 4 + - - end - 2 - - - - - True - True - True - Find - app.search - search_icon - True - - - end - 3 - - - - - Save - True - True - True - app.save - - - end - 4 - From 8ac8728e3b900928f57cff46800eb4d16e8ae57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Sat, 23 Nov 2019 02:11:32 +0100 Subject: [PATCH 05/13] implement hack to fade away the headerbar --- data/ui/Window.ui | 9 +- uberwriter/headerbars.py | 174 ++++++++++++++++++++++---------------- uberwriter/main_window.py | 59 ++++++++++++- uberwriter/text_view.py | 19 +++-- 4 files changed, 169 insertions(+), 92 deletions(-) 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): From 87e3cc127b19d5a4ffaa766aecad04607737499d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Sat, 23 Nov 2019 02:13:10 +0100 Subject: [PATCH 06/13] add flatpak to gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8be02eb..09c9621 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ build/ .vscode/ .idea/ builddir/* -build-aux/* \ No newline at end of file +build-aux/* +flatpak/* From ff579b956fe5f313e70b7b7ab2ef9b6cc0315686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Mon, 25 Nov 2019 13:30:04 +0100 Subject: [PATCH 07/13] update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 09c9621..8aa3903 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ *.pyc __pycache__/ +_build/* build/ *.*~ .vscode/ From 48e48d95def0ea39c62724d5332f1dd744c88376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Mon, 9 Dec 2019 02:14:52 +0100 Subject: [PATCH 08/13] add autohide-headerbar toggle on settings --- data/de.wolfvollprecht.UberWriter.gschema.xml | 6 +-- data/ui/Preferences.ui | 46 +++++++++++++++---- uberwriter/preferences_dialog.py | 8 ++++ 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/data/de.wolfvollprecht.UberWriter.gschema.xml b/data/de.wolfvollprecht.UberWriter.gschema.xml index 664870c..34f9300 100644 --- a/data/de.wolfvollprecht.UberWriter.gschema.xml +++ b/data/de.wolfvollprecht.UberWriter.gschema.xml @@ -62,11 +62,11 @@ Input format to use when previewing and exporting using Pandoc. - + true - Allow Uberwriter to poll cursor motion + Autohide Headerbar - Hide the header and status bars if the cursor is not moving. + Hide the header and status bars when typing. diff --git a/data/ui/Preferences.ui b/data/ui/Preferences.ui index d02cdd7..49bff6a 100644 --- a/data/ui/Preferences.ui +++ b/data/ui/Preferences.ui @@ -99,7 +99,7 @@ 0 - 2 + 3 @@ -110,7 +110,7 @@ 2 - 2 + 3 @@ -123,7 +123,7 @@ 0 - 3 + 4 @@ -134,7 +134,7 @@ 2 - 3 + 4 @@ -147,7 +147,7 @@ 0 - 4 + 5 @@ -158,7 +158,7 @@ 2 - 4 + 5 @@ -171,7 +171,7 @@ 0 - 5 + 6 @@ -184,7 +184,7 @@ 2 - 5 + 6 @@ -196,9 +196,37 @@ 1 - 5 + 6 + + + True + False + Autohide header and statusbars while typing + start + Autohide headerbar + right + + + 0 + 2 + + + + + True + True + end + + + 2 + 2 + + + + + diff --git a/uberwriter/preferences_dialog.py b/uberwriter/preferences_dialog.py index 5158298..dbccf71 100644 --- a/uberwriter/preferences_dialog.py +++ b/uberwriter/preferences_dialog.py @@ -75,6 +75,10 @@ class PreferencesDialog: self.dark_mode_switch.set_active(self.settings.get_value("dark-mode")) self.dark_mode_switch.connect("state-set", self.on_dark_mode) + self.autohide_headerbar_switch = self.builder.get_object("autohide_headerbar_switch") + self.autohide_headerbar_switch.set_active(self.settings.get_value("autohide-headerbar")) + self.autohide_headerbar_switch.connect("state-set", self.on_autohide_headerbar) + self.spellcheck_switch = self.builder.get_object("spellcheck_switch") self.spellcheck_switch.set_active(self.settings.get_value("spellcheck")) self.spellcheck_switch.connect("state-set", self.on_spellcheck) @@ -123,6 +127,10 @@ class PreferencesDialog: self.dark_mode_auto_switch.set_active(GLib.Variant.new_boolean(False)) return False + def on_autohide_headerbar(self, _, state): + self.settings.set_boolean("autohide-headerbar", state) + return False + def on_spellcheck(self, _, state): self.settings.set_boolean("spellcheck", state) return False From 151809ae9bf06c937b25d48d9832a1802d085a76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Mon, 9 Dec 2019 02:15:09 +0100 Subject: [PATCH 09/13] add background color to inline buttons --- data/media/css/gtk/base.css | 1 + 1 file changed, 1 insertion(+) diff --git a/data/media/css/gtk/base.css b/data/media/css/gtk/base.css index cc3176f..01671f3 100644 --- a/data/media/css/gtk/base.css +++ b/data/media/css/gtk/base.css @@ -107,6 +107,7 @@ .inline-button { color: alpha(@theme_fg_color, 0.6); + background-color: alpha(@theme_base_color, 0.9); text-shadow: inherit; box-shadow: initial; background-clip: initial; From 97e809a5769c1729af50af744be3e226b869cad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20Genov=C3=A9s?= Date: Mon, 9 Dec 2019 02:16:13 +0100 Subject: [PATCH 10/13] rework the autohiding mechanis for headerbars --- data/ui/Window.ui | 40 +++++++---- uberwriter/main_window.py | 135 +++++++++++++++++++------------------- 2 files changed, 93 insertions(+), 82 deletions(-) diff --git a/data/ui/Window.ui b/data/ui/Window.ui index 466d1b3..5007bdf 100644 --- a/data/ui/Window.ui +++ b/data/ui/Window.ui @@ -6,7 +6,6 @@ True False - 12 edit-find-replace-symbolic @@ -30,12 +29,12 @@ False gtk-spell-check - + FullscreenOverlay True False - + True False @@ -71,10 +70,9 @@ False True - + True False - vertical True @@ -87,15 +85,15 @@ - False - True - 0 + -1 - + True False + GDK_ENTER_NOTIFY_MASK | GDK_STRUCTURE_MASK + end crossfade 750 True @@ -116,11 +114,6 @@ - - False - True - 1 - @@ -236,6 +229,7 @@ True False + True aA @@ -416,10 +410,28 @@ 1 False start + False + True + + + 60 + True + False + start + False + True + + + + + + 1 + + diff --git a/uberwriter/main_window.py b/uberwriter/main_window.py index 55646f6..4c12258 100644 --- a/uberwriter/main_window.py +++ b/uberwriter/main_window.py @@ -74,7 +74,7 @@ class MainWindow(StyledWindow): builder = Gtk.Builder() builder.add_from_resource( "/de/wolfvollprecht/UberWriter/ui/Window.ui") - root = builder.get_object("FullscreenOverlay") + root = builder.get_object("AppOverlay") self.connect("delete-event", self.on_delete_called) self.add(root) @@ -102,11 +102,6 @@ class MainWindow(StyledWindow): self.title_end = " – UberWriter" self.set_headerbar_title("New File" + self.title_end) - self.timestamp_last_mouse_motion = 0 - if self.settings.get_value("poll-motion"): - self.connect("motion-notify-event", self.on_motion_notify) - GObject.timeout_add(3000, self.poll_for_motion) - self.accel_group = Gtk.AccelGroup() self.add_accel_group(self.accel_group) @@ -131,9 +126,10 @@ class MainWindow(StyledWindow): editor = builder.get_object('editor') self.preview_handler = PreviewHandler(self, content, editor, self.text_view) - # Setup header/stats bar hide after 3 seconds - self.top_bottom_bars_visible = True - self.was_motion = True + # Setup header/stats bar + self.headerbar_visible = True + self.bottombar_visible = True + self.previewbars_visible = True self.buffer_modified_for_status_bar = False # some people seems to have performance problems with the overlay. @@ -163,6 +159,14 @@ class MainWindow(StyledWindow): ### self.searchreplace = SearchAndReplace(self, self.text_view, builder) + # EventBoxes + + self.headerbar_eventbox = builder.get_object("HeaderbarEventbox") + self.headerbar_eventbox.connect('enter_notify_event', + self.reveal_headerbar_bottombar) + + self.stats_revealer.connect('enter_notify_event', self.reveal_bottombar) + 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 @@ -192,6 +196,8 @@ class MainWindow(StyledWindow): self.set_headerbar_title("* " + title) self.buffer_modified_for_status_bar = True + if self.settings.get_value("autohide-headerbar"): + self.hide_headerbar_bottombar() def set_fullscreen(self, state): """Puts the application in fullscreen mode and show/hides @@ -555,72 +561,62 @@ class MainWindow(StyledWindow): return self.load_file(data) - def poll_for_motion(self): - """check if the user has moved the cursor to show the headerbar - - Returns: - True -- Gtk things - """ - - if (not self.was_motion - and self.buffer_modified_for_status_bar - and self.text_view.props.has_focus): - self.reveal_top_bottom_bars(False) - - self.was_motion = False - return True - - def on_motion_notify(self, _widget, event, _data=None): - """check the motion of the mouse to fade in the headerbar - """ - now = event.get_time() - if now - self.timestamp_last_mouse_motion > 150: - # filter out accidental motions - self.timestamp_last_mouse_motion = now - return - if now - self.timestamp_last_mouse_motion < 100: - # filter out accidental motion - return - if now - self.timestamp_last_mouse_motion > 100: - # react on motion by fading in headerbar and statusbar - self.reveal_top_bottom_bars(True) - self.was_motion = True - def focus_out(self, _widget, _data=None): """events called when the window losses focus """ - self.reveal_top_bottom_bars(True) + self.reveal_headerbar_bottombar() - 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 + def reveal_headerbar_bottombar(self, _widget=None, _data=None): - # TODO: rework this logic? - - def reveal_hb(): + 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: - 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) + self.reveal_bottombar() + + if not self.headerbar_visible: + self.dm_headerbar.hide_dm_hb() + GLib.timeout_add(400, __reveal_hb) + + self.headerbar_visible = True + + if not self.previewbars_visible: for revealer in self.preview_handler.get_top_bottom_bar_revealers(): - revealer.set_reveal_child(reveal) - self.top_bottom_bars_visible = reveal - self.buffer_modified_for_status_bar = reveal + revealer.set_reveal_child(True) + + self.previewbars_visible = True + + def reveal_bottombar(self, _widget=None, _data=None): + + if not self.bottombar_visible: + self.stats_revealer.set_reveal_child(True) + + self.bottombar_visible = True + + self.buffer_modified_for_status_bar = True + + def hide_headerbar_bottombar(self): + + if self.headerbar_visible: + self.headerbar.hb_revealer.set_reveal_child(False) + self.dm_headerbar.show_dm_hb() + self.get_style_context().add_class("focus") + + self.headerbar_visible = False + + if self.bottombar_visible: + self.stats_revealer.set_reveal_child(False) + + self.bottombar_visible = False + + if self.previewbars_visible: + for revealer in self.preview_handler.get_top_bottom_bar_revealers(): + revealer.set_reveal_child(False) + + self.previewbars_visible = False + + self.buffer_modified_for_status_bar = False def draw_gradient(self, _widget, cr): """draw fading gradient over the top and the bottom of the @@ -668,13 +664,16 @@ class MainWindow(StyledWindow): self.destroy() return - def set_headerbar_title(self, title, subtitle=""): + def set_headerbar_title(self, title, subtitle=None): """set the desired headerbar title """ self.headerbar.hb.props.title = title + self.dm_headerbar.hb.props.title = title self.fs_headerbar.hb.props.title = title - self.headerbar.hb.props.subtitle = subtitle - self.fs_headerbar.hb.props.subtitle = subtitle + if subtitle: + self.headerbar.hb.props.subtitle = subtitle + self.dm_headerbar.hb.props.subtitle = subtitle + self.fs_headerbar.hb.props.subtitle = subtitle self.set_title(title) def set_filename(self, filename=None): From 5a78d75668861736f107f0fa5c8228d45ec26fb0 Mon Sep 17 00:00:00 2001 From: Manuel Genoves Date: Tue, 10 Dec 2019 17:43:02 +0100 Subject: [PATCH 11/13] fix fullscreen issues --- data/ui/Window.ui | 3 +-- uberwriter/headerbars.py | 4 ++-- uberwriter/main_window.py | 3 +++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/data/ui/Window.ui b/data/ui/Window.ui index 5007bdf..21daefa 100644 --- a/data/ui/Window.ui +++ b/data/ui/Window.ui @@ -78,6 +78,7 @@ True True True + 6 True True @@ -410,8 +411,6 @@ 1 False start - False - True diff --git a/uberwriter/headerbars.py b/uberwriter/headerbars.py index ba81346..a7b1e01 100644 --- a/uberwriter/headerbars.py +++ b/uberwriter/headerbars.py @@ -79,12 +79,12 @@ class FullscreenHeaderbar(BaseHeaderbar): 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): + def show_fs_hb(self, _widget=None, _data=None): """show headerbar of the fullscreen mode """ self.hb_revealer.set_reveal_child(True) - def hide_fs_hb(self, _widget, _data=None): + def hide_fs_hb(self, _widget=None, _data=None): """hide headerbar of the fullscreen mode """ if self.menu_button.get_active(): diff --git a/uberwriter/main_window.py b/uberwriter/main_window.py index 4c12258..f47d843 100644 --- a/uberwriter/main_window.py +++ b/uberwriter/main_window.py @@ -210,9 +210,12 @@ class MainWindow(StyledWindow): if state.get_boolean(): self.fullscreen() self.fs_headerbar.events.show() + self.fs_headerbar.hide_fs_hb() + self.headerbar_eventbox.hide() else: self.unfullscreen() self.fs_headerbar.events.hide() + self.headerbar_eventbox.show() self.text_view.grab_focus() def set_focus_mode(self, state): From d56623bfbdbc43e9cdb6c12b6e4d2896fdb446e0 Mon Sep 17 00:00:00 2001 From: Manuel Genoves Date: Tue, 10 Dec 2019 18:00:37 +0100 Subject: [PATCH 12/13] tweak headerbar reveal settings --- data/ui/Headerbar.ui | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/ui/Headerbar.ui b/data/ui/Headerbar.ui index 0a2f6aa..ce65cd8 100644 --- a/data/ui/Headerbar.ui +++ b/data/ui/Headerbar.ui @@ -17,8 +17,7 @@ True False start - slide-up - 750 + 350 True From 9216db1b804be2204a580498fe6b24329b6be9c0 Mon Sep 17 00:00:00 2001 From: Manuel Genoves Date: Wed, 11 Dec 2019 00:57:21 +0100 Subject: [PATCH 13/13] prevent headerbar to hide on open recents menu --- uberwriter/headerbars.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uberwriter/headerbars.py b/uberwriter/headerbars.py index a7b1e01..f0203f3 100644 --- a/uberwriter/headerbars.py +++ b/uberwriter/headerbars.py @@ -78,6 +78,7 @@ class FullscreenHeaderbar(BaseHeaderbar): 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) + self.recents_button.get_popover().connect('closed', self.hide_fs_hb) def show_fs_hb(self, _widget=None, _data=None): """show headerbar of the fullscreen mode @@ -87,7 +88,8 @@ class FullscreenHeaderbar(BaseHeaderbar): def hide_fs_hb(self, _widget=None, _data=None): """hide headerbar of the fullscreen mode """ - if self.menu_button.get_active(): + if (self.menu_button.get_active() or + self.recents_button.get_active()): pass else: self.hb_revealer.set_reveal_child(False)