Manuel Genovés 2019-12-25 21:34:56 +01:00
commit 9d59118afd
12 changed files with 470 additions and 250 deletions

24
.gitignore vendored
View File

@ -1,26 +1,10 @@
build/lib.linux-x86_64-2.7
*.pyc *.pyc
__pycache__/ __pycache__/
_build/*
build/ 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/ .vscode/
.idea/ .idea/
*.glade~
dist/uberwriter-2.0b0-py3.7.egg
builddir/* builddir/*
dist/ build-aux/*
uberwriter.egg-info flatpak/*
build-aux/flatpak/.flatpak-builder/*

View File

@ -62,11 +62,11 @@
Input format to use when previewing and exporting using Pandoc. Input format to use when previewing and exporting using Pandoc.
</description> </description>
</key> </key>
<key name='poll-motion' type='b'> <key name='autohide-headerbar' type='b'>
<default>true</default> <default>true</default>
<summary>Allow Uberwriter to poll cursor motion</summary> <summary>Autohide Headerbar</summary>
<description> <description>
Hide the header and status bars if the cursor is not moving. Hide the header and status bars when typing.
</description> </description>
</key> </key>
<key name='open-file-path' type='s'> <key name='open-file-path' type='s'>

View File

@ -27,6 +27,19 @@
caret-color: @theme_fg_color; 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 { #titlebar-revealer {
padding: 0; padding: 0;
} }
@ -94,6 +107,7 @@
.inline-button { .inline-button {
color: alpha(@theme_fg_color, 0.6); color: alpha(@theme_fg_color, 0.6);
background-color: alpha(@theme_base_color, 0.9);
text-shadow: inherit; text-shadow: inherit;
box-shadow: initial; box-shadow: initial;
background-clip: initial; background-clip: initial;

View File

@ -9,6 +9,7 @@
<file compressed="true" preprocess="xml-stripblanks">ui/Recents.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Recents.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Shortcuts.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Shortcuts.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Window.ui</file> <file compressed="true" preprocess="xml-stripblanks">ui/Window.ui</file>
<file compressed="true" preprocess="xml-stripblanks">ui/Headerbar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">About.ui</file> <file compressed="true" preprocess="xml-stripblanks">About.ui</file>
</gresource> </gresource>
</gresources> </gresources>

View File

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkImage" id="exit_fs_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">view-restore-symbolic</property>
</object>
<object class="GtkImage" id="search_icon">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="icon_name">system-search-symbolic</property>
</object>
<object class="GtkRevealer" id="titlebar_revealer">
<property name="name">titlebar-revealer</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="transition_duration">350</property>
<property name="reveal_child">True</property>
<signal name="size-allocate" handler="header_size_allocate" swapped="no"/>
<child>
<object class="GtkHeaderBar" id="Headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">New</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.new</property>
</object>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Open</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.open</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="recents_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Open Recent</property>
<property name="action_name">app.on_open_recent</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<style>
<class name="linked"/>
</style>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="exit_fs_button">
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="no_show_all">True</property>
<property name="action_name">app.fullscreen</property>
<property name="image">exit_fs_icon</property>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkMenuButton" id="menu_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Menu</property>
<property name="icon_name">open-menu-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Find</property>
<property name="action_name">app.search</property>
<property name="image">search_icon</property>
<property name="always_show_image">True</property>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkButton">
<property name="label" translatable="yes">Save</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="action_name">app.save</property>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -99,7 +99,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -110,7 +110,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="top_attach">2</property> <property name="top_attach">3</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -123,7 +123,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">3</property> <property name="top_attach">4</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -134,7 +134,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="top_attach">3</property> <property name="top_attach">4</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -147,7 +147,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">4</property> <property name="top_attach">5</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -158,7 +158,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="top_attach">4</property> <property name="top_attach">5</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -171,7 +171,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">0</property> <property name="left_attach">0</property>
<property name="top_attach">5</property> <property name="top_attach">6</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -184,7 +184,7 @@
</object> </object>
<packing> <packing>
<property name="left_attach">2</property> <property name="left_attach">2</property>
<property name="top_attach">5</property> <property name="top_attach">6</property>
</packing> </packing>
</child> </child>
<child> <child>
@ -196,9 +196,37 @@
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="left_attach">1</property>
<property name="top_attach">5</property> <property name="top_attach">6</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkLabel" id="autohide_headerbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Autohide header and statusbars while typing</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Autohide headerbar</property>
<property name="justify">right</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSwitch" id="autohide_headerbar_switch">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">end</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child> <child>
<placeholder/> <placeholder/>
</child> </child>

View File

@ -17,7 +17,10 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">start</property> <property name="valign">start</property>
<property name="margin">6</property> <property name="margin_left">6</property>
<property name="margin_right">6</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="filter">recent_md_filter</property> <property name="filter">recent_md_filter</property>
<property name="limit">20</property> <property name="limit">20</property>
<property name="show_icons">False</property> <property name="show_icons">False</property>

View File

@ -6,7 +6,6 @@
<object class="GtkImage" id="edit-find-replace"> <object class="GtkImage" id="edit-find-replace">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="xpad">12</property>
<property name="icon_name">edit-find-replace-symbolic</property> <property name="icon_name">edit-find-replace-symbolic</property>
</object> </object>
<object class="GtkImage" id="go-up"> <object class="GtkImage" id="go-up">
@ -30,12 +29,12 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="stock">gtk-spell-check</property> <property name="stock">gtk-spell-check</property>
</object> </object>
<object class="GtkOverlay" id="FullscreenOverlay"> <object class="GtkOverlay" id="AppOverlay">
<property name="name">FullscreenOverlay</property> <property name="name">FullscreenOverlay</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
<object class="GtkGrid" id="grid1"> <object class="GtkGrid" id="app_grid">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<child> <child>
@ -71,15 +70,15 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="homogeneous">True</property> <property name="homogeneous">True</property>
<child> <child>
<object class="GtkBox" id="editor"> <object class="GtkOverlay" id="editor">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkScrolledWindow" id="editor_scrolledwindow"> <object class="GtkScrolledWindow" id="editor_scrolledwindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="margin_top">6</property>
<property name="hexpand">True</property> <property name="hexpand">True</property>
<property name="vexpand">True</property> <property name="vexpand">True</property>
<child> <child>
@ -87,15 +86,15 @@
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="index">-1</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child type="overlay">
<object class="GtkRevealer" id="editor_stats_revealer"> <object class="GtkRevealer" id="editor_stats_revealer">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="events">GDK_ENTER_NOTIFY_MASK | GDK_STRUCTURE_MASK</property>
<property name="valign">end</property>
<property name="transition_type">crossfade</property> <property name="transition_type">crossfade</property>
<property name="transition_duration">750</property> <property name="transition_duration">750</property>
<property name="reveal_child">True</property> <property name="reveal_child">True</property>
@ -116,11 +115,6 @@
</object> </object>
</child> </child>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child> </child>
</object> </object>
<packing> <packing>
@ -236,6 +230,7 @@
<object class="GtkBox" id="searchtools_box"> <object class="GtkBox" id="searchtools_box">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="homogeneous">True</property>
<child> <child>
<object class="GtkToggleButton" id="case_sensitive"> <object class="GtkToggleButton" id="case_sensitive">
<property name="label" translatable="yes">aA</property> <property name="label" translatable="yes">aA</property>
@ -414,27 +409,28 @@
<child type="overlay"> <child type="overlay">
<object class="GtkEventBox" id="FullscreenEventbox"> <object class="GtkEventBox" id="FullscreenEventbox">
<property name="height_request">1</property> <property name="height_request">1</property>
<property name="visible">True</property>
<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>
<object class="GtkHeaderBar" id="FullscreenHeaderbar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<child>
<placeholder/>
</child>
</object>
</child>
</object>
</child> </child>
</object> </object>
</child> </child>
<child type="overlay">
<object class="GtkEventBox" id="HeaderbarEventbox">
<property name="height_request">60</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">start</property>
<property name="visible_window">False</property>
<property name="above_child">True</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="index">1</property>
</packing>
</child>
</object> </object>
</interface> </interface>

View File

@ -16,41 +16,123 @@
"""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):
self.hb = Gtk.HeaderBar().new() #pylint: disable=C0103
self.hb.props.show_close_button = True
self.hb.get_style_context().add_class("titlebar")
self.hb_revealer = Gtk.Revealer(name='titlebar-revealer') BaseHeaderbar.__init__(self, app)
self.hb_revealer.add(self.hb)
self.hb_revealer.props.transition_duration = 750 self.hb.set_show_close_button(True)
self.hb_revealer.props.transition_type = Gtk.RevealerTransitionType.CROSSFADE
self.hb_revealer.show() add_menus(self, app)
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)
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
"""
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=None, _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() or
self.recents_button.get_active()):
pass
else:
self.hb_revealer.set_reveal_child(False)
self.btns = main_buttons(app)
pack_main_buttons(self.hb, self.btns)
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:
@ -62,10 +144,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)
@ -77,76 +160,20 @@ class PreviewHeaderbar:
self.hb.show_all() self.hb.show_all()
class FullscreenHeaderbar: def add_menus(headerbar, app):
"""Sets up and manages the fullscreen headerbar and his events """ Add menu models to hb buttons
""" """
def __init__(self, builder, app): # Add menu model to the menu button
self.events = builder.get_object("FullscreenEventbox")
self.revealer = builder.get_object(
"FullscreenHbPlaceholder")
self.revealer.show()
self.revealer.set_reveal_child(False)
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)
self.hb.show_all()
# 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)
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.btns.menu.get_active():
pass
else:
self.revealer.set_reveal_child(False)
def main_buttons(app):
"""constructor for the headerbar buttons
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())
builder_window_menu = Gtk.Builder() builder_window_menu = Gtk.Builder()
builder_window_menu.add_from_resource( builder_window_menu.add_from_resource(
"/de/wolfvollprecht/UberWriter/ui/Menu.ui") "/de/wolfvollprecht/UberWriter/ui/Menu.ui")
model = builder_window_menu.get_object("Menu") model = builder_window_menu.get_object("Menu")
open_button = Gtk.Button().new_with_label(_("Open")) headerbar.menu_button.set_menu_model(model)
open_button.set_action_name("app.open")
# Add recents menu to the open recents button
recents_builder = Gtk.Builder() recents_builder = Gtk.Builder()
recents_builder.add_from_resource( recents_builder.add_from_resource(
@ -159,44 +186,5 @@ def main_buttons(app):
recents_wd = recents_builder.get_object("recent_md_widget") recents_wd = recents_builder.get_object("recent_md_widget")
recents_wd.connect('item-activated', app.on_open_recent) recents_wd.connect('item-activated', app.on_open_recent)
recents_button = Gtk.MenuButton().new() headerbar.recents_button.set_popover(recents)
recents_button.set_image(Gtk.Image.new_from_icon_name("pan-down-symbolic", headerbar.recents_button.set_sensitive(True)
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)

View File

@ -74,7 +74,7 @@ class MainWindow(StyledWindow):
builder = Gtk.Builder() builder = Gtk.Builder()
builder.add_from_resource( builder.add_from_resource(
"/de/wolfvollprecht/UberWriter/ui/Window.ui") "/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.connect("delete-event", self.on_delete_called)
self.add(root) self.add(root)
@ -84,18 +84,24 @@ 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)
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.accel_group = Gtk.AccelGroup()
self.add_accel_group(self.accel_group) self.add_accel_group(self.accel_group)
@ -103,6 +109,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()
@ -119,9 +126,10 @@ class MainWindow(StyledWindow):
editor = builder.get_object('editor') editor = builder.get_object('editor')
self.preview_handler = PreviewHandler(self, content, editor, self.text_view) self.preview_handler = PreviewHandler(self, content, editor, self.text_view)
# Setup header/stats bar hide after 3 seconds # Setup header/stats bar
self.top_bottom_bars_visible = True self.headerbar_visible = True
self.was_motion = True self.bottombar_visible = True
self.previewbars_visible = True
self.buffer_modified_for_status_bar = False self.buffer_modified_for_status_bar = False
# some people seems to have performance problems with the overlay. # some people seems to have performance problems with the overlay.
@ -151,6 +159,32 @@ class MainWindow(StyledWindow):
### ###
self.searchreplace = SearchAndReplace(self, self.text_view, builder) 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
"""
# 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
@ -162,6 +196,8 @@ class MainWindow(StyledWindow):
self.set_headerbar_title("* " + title) self.set_headerbar_title("* " + title)
self.buffer_modified_for_status_bar = True self.buffer_modified_for_status_bar = True
if self.settings.get_value("autohide-headerbar"):
self.hide_headerbar_bottombar()
def set_fullscreen(self, state): def set_fullscreen(self, state):
"""Puts the application in fullscreen mode and show/hides """Puts the application in fullscreen mode and show/hides
@ -174,16 +210,19 @@ class MainWindow(StyledWindow):
if state.get_boolean(): if state.get_boolean():
self.fullscreen() self.fullscreen()
self.fs_headerbar.events.show() self.fs_headerbar.events.show()
self.fs_headerbar.hide_fs_hb()
self.headerbar_eventbox.hide()
else: else:
self.unfullscreen() self.unfullscreen()
self.fs_headerbar.events.hide() self.fs_headerbar.events.hide()
self.headerbar_eventbox.show()
self.text_view.grab_focus() self.text_view.grab_focus()
def set_focus_mode(self, state): def set_focus_mode(self, state):
"""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,57 +558,68 @@ 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:
return return
self.load_file(data) 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): def focus_out(self, _widget, _data=None):
"""events called when the window losses focus """events called when the window losses focus
""" """
self.reveal_top_bottom_bars(True) self.reveal_headerbar_bottombar()
def reveal_top_bottom_bars(self, reveal): def reveal_headerbar_bottombar(self, _widget=None, _data=None):
if self.top_bottom_bars_visible != reveal:
self.headerbar.hb_revealer.set_reveal_child(reveal) def __reveal_hb():
self.stats_revealer.set_reveal_child(reveal) self.headerbar.hb_revealer.set_reveal_child(True)
self.get_style_context().remove_class("focus")
return False
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(): for revealer in self.preview_handler.get_top_bottom_bar_revealers():
revealer.set_reveal_child(reveal) revealer.set_reveal_child(True)
self.top_bottom_bars_visible = reveal
self.buffer_modified_for_status_bar = reveal 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): def draw_gradient(self, _widget, cr):
"""draw fading gradient over the top and the bottom of the """draw fading gradient over the top and the bottom of the
@ -617,13 +667,16 @@ class MainWindow(StyledWindow):
self.destroy() self.destroy()
return return
def set_headerbar_title(self, title, subtitle=""): def set_headerbar_title(self, title, subtitle=None):
"""set the desired headerbar title """set the desired headerbar title
""" """
self.headerbar.hb.props.title = title self.headerbar.hb.props.title = title
self.dm_headerbar.hb.props.title = title
self.fs_headerbar.hb.props.title = title self.fs_headerbar.hb.props.title = title
self.headerbar.hb.props.subtitle = subtitle if subtitle:
self.fs_headerbar.hb.props.subtitle = 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) self.set_title(title)
def set_filename(self, filename=None): def set_filename(self, filename=None):

View File

@ -75,6 +75,10 @@ class PreferencesDialog:
self.dark_mode_switch.set_active(self.settings.get_value("dark-mode")) self.dark_mode_switch.set_active(self.settings.get_value("dark-mode"))
self.dark_mode_switch.connect("state-set", self.on_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 = self.builder.get_object("spellcheck_switch")
self.spellcheck_switch.set_active(self.settings.get_value("spellcheck")) self.spellcheck_switch.set_active(self.settings.get_value("spellcheck"))
self.spellcheck_switch.connect("state-set", self.on_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)) self.dark_mode_auto_switch.set_active(GLib.Variant.new_boolean(False))
return False return False
def on_autohide_headerbar(self, _, state):
self.settings.set_boolean("autohide-headerbar", state)
return False
def on_spellcheck(self, _, state): def on_spellcheck(self, _, state):
self.settings.set_boolean("spellcheck", state) self.settings.set_boolean("spellcheck", state)
return False return False

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):