implement hack to fade away the headerbar

ui
Manuel Genovés 2019-11-23 02:11:32 +01:00
parent f766c3703d
commit 8ac8728e3b
4 changed files with 169 additions and 92 deletions

View File

@ -417,14 +417,7 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">start</property> <property name="valign">start</property>
<child> <child>
<object class="GtkRevealer" id="FullscreenHbPlaceholder"> <placeholder/>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<child>
<placeholder/>
</child>
</object>
</child> </child>
</object> </object>
</child> </child>

View File

@ -16,46 +16,121 @@
"""Manage all the headerbars related stuff """Manage all the headerbars related stuff
""" """
from collections import namedtuple
from gettext import gettext as _
import gi import gi
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
from gi.repository import Gtk from gi.repository import Gtk, GLib
from uberwriter.helpers import get_descendant 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 """Sets up the main application headerbar
""" """
def __init__(self, app): def __init__(self, app):
builder = Gtk.Builder() BaseHeaderbar.__init__(self, app)
builder.add_from_resource(
"/de/wolfvollprecht/UberWriter/ui/Headerbar.ui")
self.hb = builder.get_object("Headerbar") self.hb.set_show_close_button(True)
self.menu_button = builder.get_object("menu_button")
self.recents_button = builder.get_object("recents_button")
add_menus(self, app) add_menus(self, app)
self.hb_revealer = Gtk.Revealer(name='titlebar-revealer') self.hb_revealer.props.transition_duration = 0
self.hb_revealer.add(self.hb)
self.hb_revealer.props.transition_duration = 750
self.hb_revealer.props.transition_type = Gtk.RevealerTransitionType.CROSSFADE class FullscreenHeaderbar(BaseHeaderbar):
self.hb_revealer.show() """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_revealer.set_reveal_child(True)
self.hb_container = Gtk.Frame(name='titlebar-container') def hide_fs_hb(self, _widget, _data=None):
self.hb_container.set_shadow_type(Gtk.ShadowType.NONE) """hide headerbar of the fullscreen mode
self.hb_container.add(self.hb_revealer) """
self.hb_container.show() 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: class PreviewHeaderbar:
@ -67,10 +142,11 @@ class PreviewHeaderbar:
self.hb.props.show_close_button = True self.hb.props.show_close_button = True
self.hb.get_style_context().add_class("titlebar") 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.add(self.hb)
self.hb_revealer.props.transition_duration = 750 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.show()
self.hb_revealer.set_reveal_child(True) self.hb_revealer.set_reveal_child(True)
@ -82,55 +158,9 @@ class PreviewHeaderbar:
self.hb.show_all() 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): def add_menus(headerbar, app):
""" Add menu models to hb buttons
"""
# Add menu model to the menu button # Add menu model to the menu button

View File

@ -84,10 +84,21 @@ class MainWindow(StyledWindow):
self.settings = Settings.new() self.settings = Settings.new()
# Headerbars # Headerbars
self.last_height = 0
self.headerbar = headerbars.MainHeaderbar(app) 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) 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.title_end = " UberWriter"
self.set_headerbar_title("New File" + self.title_end) self.set_headerbar_title("New File" + self.title_end)
@ -103,6 +114,7 @@ class MainWindow(StyledWindow):
# Setup text editor # Setup text editor
self.text_view = TextView(self.settings.get_int("characters-per-line")) 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.connect('focus-out-event', self.focus_out)
self.text_view.get_buffer().connect('changed', self.on_text_changed) self.text_view.get_buffer().connect('changed', self.on_text_changed)
self.text_view.show() self.text_view.show()
@ -151,6 +163,24 @@ class MainWindow(StyledWindow):
### ###
self.searchreplace = SearchAndReplace(self, self.text_view, builder) 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): def on_text_changed(self, *_args):
"""called when the text changes, sets the self.did_change to true and """called when the text changes, sets the self.did_change to true and
updates the title and the counters to reflect that updates the title and the counters to reflect that
@ -183,7 +213,7 @@ class MainWindow(StyledWindow):
"""toggle focusmode """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() self.text_view.grab_focus()
def set_hemingway_mode(self, state): def set_hemingway_mode(self, state):
@ -519,7 +549,6 @@ class MainWindow(StyledWindow):
def open_recent(self, _widget, data=None): def open_recent(self, _widget, data=None):
"""open the given recent document """open the given recent document
""" """
print("open")
if data: if data:
if self.check_change() == Gtk.ResponseType.CANCEL: if self.check_change() == Gtk.ResponseType.CANCEL:
@ -563,8 +592,30 @@ class MainWindow(StyledWindow):
self.reveal_top_bottom_bars(True) self.reveal_top_bottom_bars(True)
def reveal_top_bottom_bars(self, reveal): 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: 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) self.stats_revealer.set_reveal_child(reveal)
for revealer in self.preview_handler.get_top_bottom_bar_revealers(): for revealer in self.preview_handler.get_top_bottom_bar_revealers():
revealer.set_reveal_child(reveal) revealer.set_reveal_child(reveal)

View File

@ -54,6 +54,9 @@ class TextView(Gtk.TextView):
self.set_pixels_inside_wrap(8) self.set_pixels_inside_wrap(8)
self.get_style_context().add_class('uberwriter-editor') self.get_style_context().add_class('uberwriter-editor')
self.set_margin_left(8)
self.set_margin_right(8)
# Text sizing # Text sizing
self.props.halign = Gtk.Align.FILL self.props.halign = Gtk.Align.FILL
self.line_chars = line_chars self.line_chars = line_chars
@ -144,7 +147,6 @@ class TextView(Gtk.TextView):
def on_size_allocate(self, *_): def on_size_allocate(self, *_):
self.update_horizontal_margin() self.update_horizontal_margin()
self.update_vertical_margin()
self.markup.update_margins_indents() self.markup.update_margins_indents()
self.queue_draw() self.queue_draw()
@ -192,14 +194,14 @@ class TextView(Gtk.TextView):
self.frozen_scroll_scale = None self.frozen_scroll_scale = None
self.queue_draw() self.queue_draw()
def set_focus_mode(self, focus_mode): def set_focus_mode(self, focus_mode, hb_height):
"""Toggle focus mode. """Toggle focus mode.
When in focus mode, the cursor sits in the middle of the text view, When in focus mode, the cursor sits in the middle of the text view,
and the surrounding text is greyed out.""" and the surrounding text is greyed out."""
self.focus_mode = focus_mode self.focus_mode = focus_mode
self.update_vertical_margin() self.update_vertical_margin(hb_size=hb_height)
self.markup.apply() self.markup.apply()
self.smooth_scroll_to() self.smooth_scroll_to()
self.set_spellcheck(self.spellcheck) self.set_spellcheck(self.spellcheck)
@ -227,13 +229,14 @@ class TextView(Gtk.TextView):
self.props.left_margin = horizontal_margin self.props.left_margin = horizontal_margin
self.props.right_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: if self.focus_mode:
height = self.get_allocation().height height = self.get_allocation().height + top_margin + hb_size
self.props.top_margin = height / 2
self.props.bottom_margin = height / 2 self.props.top_margin = height / 2 + top_margin
self.props.bottom_margin = height / 2 - top_margin
else: else:
self.props.top_margin = 80 self.props.top_margin = 80 + top_margin
self.props.bottom_margin = 64 self.props.bottom_margin = 64
def set_hemingway_mode(self, hemingway_mode): def set_hemingway_mode(self, hemingway_mode):