Commit 7c6a4369 authored by Manuel Genovés's avatar Manuel Genovés
Browse files

wip

parent 0e382ba1
Pipeline #396998 failed with stage
in 19 seconds
......@@ -13,21 +13,19 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Handy', '1')
from gi.repository import GLib, Gio, Gtk, Gdk, Handy
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import GLib, Gio, Gtk, Gdk, Adw
from apostrophe.main_window import MainWindow
from apostrophe.settings import Settings
from apostrophe.helpers import set_up_logging
from apostrophe.preferences_dialog import ApostrophePreferencesDialog
# from apostrophe.preferences_dialog import ApostrophePreferencesDialog
from apostrophe.inhibitor import Inhibitor
from apostrophe.theme_switcher import Theme
class Application(Gtk.Application):
class Application(Adw.Application):
def __init__(self, application_id, *args, **kwargs):
super().__init__(*args, application_id=application_id,
......@@ -36,53 +34,56 @@ class Application(Gtk.Application):
self.add_main_option("verbose", b"v", GLib.OptionFlags.NONE,
GLib.OptionArg.NONE, "Verbose output", None)
# Adw.init()
# Hardcode Adwaita to prevent issues with third party themes
gtk_settings = Gtk.Settings.get_default()
self._set_theme(gtk_settings)
gtk_settings.connect("notify::gtk-theme-name", self._set_theme)
gtk_settings.connect("notify::gtk-icon-theme-name", self._set_theme)
# gtk_settings = Gtk.Settings.get_default()
self.windows = []
self.settings = Settings.new()
self.inhibitor = None
self._application_id = application_id
def do_startup(self):
Adw.Application.do_startup(self)
# Set css theme
css_provider_file = Gio.File.new_for_uri(
"resource:///org/gnome/gitlab/somas/Apostrophe/media/css/gtk/Adwaita.css")
"resource:///org/gnome/gitlab/somas/Apostrophe/media/css/gtk/style.css")
self.style_provider = Gtk.CssProvider()
self.style_provider.load_from_file(css_provider_file)
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(), self.style_provider,
css_sepia_provider_file = Gio.File.new_for_uri(
"resource:///org/gnome/gitlab/somas/Apostrophe/media/css/gtk/style-sepia.css")
self.sepia_style_provider = Gtk.CssProvider()
self.sepia_style_provider.load_from_file(css_sepia_provider_file)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(), self.style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_USER)
# Style manager
style_manager = Handy.StyleManager.get_default()
style_manager = Adw.StyleManager.get_default()
style_manager.connect("notify::dark", self._set_color_scheme)
style_manager.connect("notify::high-contrast", self._set_color_scheme)
# Set editor keybindings
# SCSS is not fit for this, so we do it in an external css file
css_bindings_file = Gio.File.new_for_uri(
"resource:///org/gnome/gitlab/somas/Apostrophe/media/css/gtk/bindings.css")
self.bindings_provider = Gtk.CssProvider()
self.bindings_provider.load_from_file(css_bindings_file)
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(), self.bindings_provider,
Gtk.STYLE_PROVIDER_PRIORITY_USER)
# css_bindings_file = Gio.File.new_for_uri(
# "resource:///org/gnome/gitlab/somas/Apostrophe/media/css/gtk/bindings.css")
# self.bindings_provider = Gtk.CssProvider()
# self.bindings_provider.load_from_file(css_bindings_file# )
# Gtk.StyleContext.add_provider_for_display(
# Gdk.Display.get_default(), self.bindings_provider,
# Gtk.STYLE_PROVIDER_PRIORITY_USER)
# Set icons
Gtk.IconTheme.get_default().add_resource_path(
Gtk.IconTheme.get_for_display(
Gdk.Display.get_default()).add_resource_path(
"/org/gnome/gitlab/somas/Apostrophe/icons"
)
Handy.init()
self.windows = []
self.settings = Settings.new()
self.inhibitor = None
self._application_id = application_id
def do_startup(self, *args, **kwargs):
Gtk.Application.do_startup(self)
color_scheme = self.settings.get_string("color-scheme")
action = Gio.SimpleAction.new_stateful(
......@@ -157,7 +158,6 @@ class Application(Gtk.Application):
def do_handle_local_options(self, options):
if options.contains("verbose") or self._application_id \
== 'org.gnome.gitlab.somas.Apostrophe.Devel':
set_up_logging(1)
return -1
......@@ -172,50 +172,20 @@ class Application(Gtk.Application):
window.present()
self.windows.append(window)
def _set_theme(self, settings, *_pspec):
# Third party themes cause issues with Apostrophe custom stylesheets
# If the user has a third party theme selected, we just change it to
# Adwaita to prevent those issues
# TODO: GTK4 - remove this
theme_name = settings.get_property("gtk-theme-name")
icon_theme_name = settings.get_property("gtk-icon-theme-name")
if (theme_name not in ["Adwaita",
"HighContrast",
"HighContrastInverse"]):
settings.set_property("gtk-theme-name", "Adwaita")
def _set_color_scheme(self, *args, **kwargs):
# TODO: GTK4 - remove this
theme = Theme.get_current()
style_manager = Handy.StyleManager.get_default()
dark = style_manager.get_dark()
sepia = theme.name == "sepia"
hc = style_manager.get_high_contrast()
sepia = Theme.get_current().name == "sepia"
if not self.windows:
return
if sepia:
if hc:
self.style_provider.load_from_file(theme.gtk_css_hc)
else:
self.style_provider.load_from_file(theme.gtk_css)
elif dark:
if hc:
self.style_provider.load_from_file(Theme.get_for_name("dark").gtk_css_hc)
else:
self.style_provider.load_from_file(Theme.get_for_name("dark").gtk_css)
Gtk.StyleContext.add_provider_for_display(
Gdk.Display.get_default(), self.sepia_style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_USER)
else:
if hc:
self.style_provider.load_from_file(Theme.get_for_name("light").gtk_css_hc)
else:
self.style_provider.load_from_file(Theme.get_for_name("light").gtk_css)
Gtk.StyleContext.remove_provider_for_display(
Gdk.Display.get_default(), self.sepia_style_provider
)
def on_settings_changed(self, settings, key):
# TODO: change this ffs
......
......@@ -28,7 +28,7 @@ from zipfile import ZipFile
import json
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gtk', '4.0')
gi.require_version('Handy', '1')
from gi.repository import Gtk, Gdk, Gio, GObject, Handy
......
import re
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gtk', '4.0')
import logging
logger = logging.getLogger('apostrophe')
......
......@@ -19,8 +19,8 @@ import gi
from gettext import gettext as _
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GLib, GObject, Handy
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, GLib, GObject, Adw
from .settings import Settings
from .theme_switcher import ThemeSwitcher
from .preview_layout_switcher import PreviewLayoutSwitcher
......@@ -36,8 +36,9 @@ class BaseHeaderbar(Gtk.Revealer):
"""
headerbar = Gtk.Template.Child()
tutorial_button = Gtk.Template.Child()
preview_layout_switcher = Gtk.Template.Child()
menu_button = Gtk.Template.Child()
#tutorial_button = Gtk.Template.Child()
#preview_layout_switcher = Gtk.Template.Child()
is_fullscreen = GObject.property(type=bool, default=False)
title = GObject.Property(type=str)
......@@ -51,11 +52,12 @@ class BaseHeaderbar(Gtk.Revealer):
super().__init__(**kwargs)
self.bind_property("is-fullscreen", self.headerbar, "show-close-button", 6)
# TODO - move to ui file, check on start
self.bind_property("is-fullscreen", self.headerbar, "show-start-title-buttons", 6)
self.bind_property("is-fullscreen", self.headerbar, "show-end-title-buttons", 6)
tutorial = GLib.Variant.new_string("resource:///org/gnome/gitlab/somas/"
"Apostrophe/media/apostrophe_markdown.md")
self.tutorial_button.set_action_target_value(tutorial)
popover = self.menu_button.get_popover()
popover.add_child(ThemeSwitcher(), "themeswitcher")
self.settings = Settings.new()
......
......@@ -27,9 +27,8 @@ import gi
import pypandoc
from gi.overrides.Pango import Pango
gi.require_version('Gtk', '3.0')
gi.require_version('Handy', '1')
from gi.repository import Gtk, Gio, Handy # pylint: disable=E0611
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio # pylint: disable=E0611
from apostrophe.settings import Settings
from apostrophe import config
......@@ -105,20 +104,20 @@ def exist_executable(command):
def liststore_from_list(str_list: List[str]):
"""return a Gtk.ListStore object of Handy.TypeValues
"""return a Gtk.ListStore object of Gtk.StringObjects
constructed after a list of strings
Arguments:
str_list {List[str]} -- a list of strings
Returns:
{Gtk.ListStore} -- a ListStore of Handy.ValueObjects
{Gtk.ListStore} -- a ListStore of Gtk.StringObject
"""
list_store = Gio.ListStore.new(Handy.ValueObject)
list_store = Gio.ListStore.new(Gtk.StringObject)
for element in str_list:
obj = Handy.ValueObject.new(element)
obj = Gtk.StringObject.new(element)
list_store.append(obj)
return list_store
......
......@@ -21,42 +21,42 @@ from dataclasses import dataclass
import chardet
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, GLib, Gio, Handy
from apostrophe.export_dialog import ExportDialog, AdvancedExportDialog
from apostrophe.preview_handler import PreviewHandler
from apostrophe.stats_handler import StatsHandler
from apostrophe.text_view import ApostropheTextView
from apostrophe.search_and_replace import ApostropheSearchBar
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gdk, GObject, GLib, Gio, Adw
# from apostrophe.export_dialog import ExportDialog, AdvancedExportDialog
from apostrophe.headerbars import BaseHeaderbar
# from apostrophe.preview_handler import PreviewHandler
# from apostrophe.stats_handler import StatsHandler
# from apostrophe.text_view import ApostropheTextView
# from apostrophe.search_and_replace import ApostropheSearchBar
from apostrophe.settings import Settings
from apostrophe.tweener import Tweener
# from apostrophe.tweener import Tweener
from apostrophe.helpers import App
from apostrophe import helpers
from .headerbars import BaseHeaderbar
LOGGER = logging.getLogger('apostrophe')
@Gtk.Template(resource_path='/org/gnome/gitlab/somas/Apostrophe/ui/Window.ui')
class MainWindow(Handy.ApplicationWindow):
class MainWindow(Adw.ApplicationWindow):
__gtype_name__ = "ApostropheWindow"
editor_scrolledwindow = Gtk.Template.Child()
save_progressbar = Gtk.Template.Child()
# editor_scrolledwindow = Gtk.Template.Child()
# save_progressbar = Gtk.Template.Child()
headerbar = Gtk.Template.Child()
headerbar_eventbox = Gtk.Template.Child()
searchbar = Gtk.Template.Child()
# headerbar_eventbox = Gtk.Template.Child()
# searchbar = Gtk.Template.Child()
stats_revealer = Gtk.Template.Child()
stats_button = Gtk.Template.Child()
flap = Gtk.Template.Child()
# TODO ??
preview_stack = Gtk.Template.Child()
text_view = Gtk.Template.Child()
editor = Gtk.Template.Child()
# flap = Gtk.Template.Child()
# # TODO ??
# preview_stack = Gtk.Template.Child()
# text_view = Gtk.Template.Child()
# editor = Gtk.Template.Child()
subtitle = GObject.Property(type=str)
is_fullscreen = GObject.Property(type=bool, default=False)
......@@ -79,67 +79,71 @@ class MainWindow(Handy.ApplicationWindow):
# Preferences
self.settings = Settings.new()
# Connect signals that we can't connect on the UI file
self.connect("close-request", self.on_close_request)
self.connect("notify::is-fullscreen", self._on_fullscreen)
# Create new, empty file
# TODO: load last opened file?
self.current = File()
# Setup text editor
self.text_view.get_buffer().connect('changed', self.on_text_changed)
self.text_view.grab_focus()
# self.text_view.get_buffer().connect('changed', self.on_text_changed)
# self.text_view.grab_focus()
# Setup save progressbar an its animator
self.progressbar_initiate_tw = Tweener(self.save_progressbar,
self.save_progressbar.set_fraction,
0, 0.125, 40)
self.progressbar_finalize_tw = Tweener(self.save_progressbar,
self.save_progressbar.set_fraction,
0.125, 1, 400)
self.progressbar_opacity_tw = Tweener(self.save_progressbar,
self.save_progressbar.set_opacity,
1, 0, 300, 200,
callback = self.save_progressbar.set_visible,
callback_arg = False)
# self.progressbar_initiate_tw = Tweener(self.save_progressbar,
# self.save_progressbar.set_fraction,
# 0, 0.125, 40)
# self.progressbar_finalize_tw = Tweener(self.save_progressbar,
# self.save_progressbar.set_fraction,
# 0.125, 1, 400)
# self.progressbar_opacity_tw = Tweener(self.save_progressbar,
# self.save_progressbar.set_opacity,
# 1, 0, 300, 200,
# callback = self.save_progressbar.set_visible,
# callback_arg = False)
# Setup stats counter
self.stats_handler = StatsHandler(self.stats_button, self.text_view)
#self.stats_handler = StatsHandler(self.stats_button, self.text_view)
# Setup preview
self.preview_handler = PreviewHandler(self, self.text_view, self.flap)
#self.preview_handler = PreviewHandler(self, self.text_view, self.flap)
# Setting up spellcheck
self.settings.bind("spellcheck", self.text_view,
"spellcheck", Gio.SettingsBindFlags.GET)
#self.settings.bind("spellcheck", self.text_view,
# "spellcheck", Gio.SettingsBindFlags.GET)
# Setting up text size
self.settings.bind("bigger-text", self.text_view,
"bigger_text", Gio.SettingsBindFlags.GET)
#self.settings.bind("bigger-text", self.text_view,
# "bigger_text", Gio.SettingsBindFlags.GET)
self.settings.bind("characters-per-line", self.text_view,
"line_chars", Gio.SettingsBindFlags.GET)
#self.settings.bind("characters-per-line", self.text_view,
# "line_chars", Gio.SettingsBindFlags.GET)
# Search and replace initialization
self.searchbar.attach(self.text_view)
#self.searchbar.attach(self.text_view)
# Actions
action = Gio.PropertyAction.new("find", self.searchbar, "search-mode-enabled")
self.add_action(action)
#action = Gio.PropertyAction.new("find", self.searchbar, "search-mode-enabled")
#self.add_action(action)
action = Gio.PropertyAction.new("find_replace", self.searchbar, "replace-mode-enabled")
self.add_action(action)
#action = Gio.PropertyAction.new("find_replace", self.searchbar, "replace-mode-enabled")
#self.add_action(action)
action = Gio.PropertyAction.new("focus_mode", self.text_view, "focus-mode")
self.add_action(action)
#action = Gio.PropertyAction.new("focus_mode", self.text_view, "focus-mode")
#self.add_action(action)
action = Gio.PropertyAction.new("hemingway_mode", self.text_view, "hemingway-mode")
self.add_action(action)
#action = Gio.PropertyAction.new("hemingway_mode", self.text_view, "hemingway-mode")
#self.add_action(action)
action = Gio.PropertyAction.new("fullscreen", self, "is-fullscreen")
self.add_action(action)
action = Gio.PropertyAction.new("preview", self, "preview")
self.add_action(action)
self.connect("notify::preview", self.toggle_preview)
#self.connect("notify::preview", self.toggle_preview)
action = Gio.SimpleAction.new("new", None)
......@@ -154,44 +158,44 @@ class MainWindow(Handy.ApplicationWindow):
action.connect("activate", self.open_from_gvariant)
self.add_action(action)
action = Gio.SimpleAction.new("save", None)
action.connect("activate", self.save_document)
self.add_action(action)
# action = Gio.SimpleAction.new("save", None)
# action.connect("activate", self.save_document)
# self.add_action(action)
action = Gio.SimpleAction.new("save_as", None)
action.connect("activate", self.save_document_as)
self.add_action(action)
# action = Gio.SimpleAction.new("save_as", None)
# action.connect("activate", self.save_document_as)
# self.add_action(action)
action = Gio.SimpleAction.new("export", GLib.VariantType("s"))
action.connect("activate", self.open_export)
self.add_action(action)
# action = Gio.SimpleAction.new("export", GLib.VariantType("s"))
# action.connect("activate", self.open_export)
# self.add_action(action)
action = Gio.SimpleAction.new("advanced_export", None)
action.connect("activate", self.open_advanced_export)
self.add_action(action)
# action = Gio.SimpleAction.new("advanced_export", None)
# action.connect("activate", self.open_advanced_export)
# self.add_action(action)
action = Gio.SimpleAction.new("copy_html", None)
action.connect("activate", self.copy_html_to_clipboard)
self.add_action(action)
# action = Gio.SimpleAction.new("copy_html", None)
# action.connect("activate", self.copy_html_to_clipboard)
# self.add_action(action)
# not really necessary but we'll keep a preview_layout property on the window
# and bind it both to the switcher and the renderer
self.preview_layout = self.settings.get_enum("preview-mode")
self.bind_property("preview_layout", self.headerbar.preview_layout_switcher,
"preview_layout", GObject.BindingFlags.BIDIRECTIONAL|GObject.BindingFlags.SYNC_CREATE)
# self.bind_property("preview_layout", self.headerbar.preview_layout_switcher,
# "preview_layout", GObject.BindingFlags.BIDIRECTIONAL|GObject.BindingFlags.SYNC_CREATE)
self.bind_property("preview_layout", self.preview_handler.preview_renderer,
"preview_layout", GObject.BindingFlags.SYNC_CREATE)
# self.bind_property("preview_layout", self.preview_handler.preview_renderer,
# "preview_layout", GObject.BindingFlags.SYNC_CREATE)
self.preview = self.settings.get_boolean("preview-active")
self.text_view.hemingway_mode = self.settings.get_boolean("hemingway-mode")
# self.text_view.hemingway_mode = self.settings.get_boolean("hemingway-mode")
self.new_document()
# TODO: change to closures on GTK4
# "Bind" scrolled window's scrollbar margin to headerbar visibility
@Gtk.Template.Callback()
#@Gtk.Template.Callback()
def headerbar_revealed_cb(self, widget, *args):
scrollbar = self.editor_scrolledwindow.get_vscrollbar()
scrollbar.set_margin_top(46 if widget.get_reveal_child() else 0)
......@@ -207,7 +211,6 @@ class MainWindow(Handy.ApplicationWindow):
if self.settings.get_value("autohide-headerbar"):
self.hide_headerbar_bottombar()
@Gtk.Template.Callback()
def _on_fullscreen(self, *args, **kwargs):
"""Puts the application in fullscreen mode and show/hides
the poller for motion in the top border
......@@ -435,6 +438,7 @@ class MainWindow(Handy.ApplicationWindow):
filechooser.destroy()
def open_from_gvariant(self, _action, gvariant):
print(gvariant.get_string())
self.load_file(Gio.File.new_for_uri(gvariant.get_string()))
def load_file(self, file=None):
......@@ -525,15 +529,15 @@ class MainWindow(Handy.ApplicationWindow):
"""create new document
"""
response = self.check_change()
if (response == Gtk.ResponseType.CANCEL or
response == Gtk.ResponseType.DELETE_EVENT):
return
self.text_view.clear()
# response = self.check_change()
# if (response == Gtk.ResponseType.CANCEL or
# response == Gtk.ResponseType.DELETE_EVENT):
# return
# self.text_view.clear()
self.did_change = False
self.current.gfile = None
self.update_headerbar_title(False, False)
# self.update_headerbar_title(False, False)
def update_default_stat(self):
self.stats_handler.update_default_stat()
......@@ -561,7 +565,7 @@ class MainWindow(Handy.ApplicationWindow):
export_dialog.set_transient_for(self)
export_dialog.show()
@Gtk.Template.Callback()
# @Gtk.Template.Callback()
def reveal_headerbar_bottombar(self, _widget=None, _data=None):
self.reveal_bottombar()
......@@ -574,7 +578,7 @@ class MainWindow(Handy.ApplicationWindow):
self.headerbar_visible = True
@Gtk.Template.Callback()
def reveal_bottombar(self, _widget=None, _data=None):
def reveal_bottombar(self, _gesture: Gtk.EventControllerMotion, _x: int, _y: int):
if not self.bottombar_visible:
self.stats_revealer.set_reveal_child(True)
......@@ -633,11 +637,10 @@ class MainWindow(Handy.ApplicationWindow):
self.settings.set_boolean("preview-active", self.preview)
self.settings.set_boolean("hemingway-mode", self.text_view.hemingway_mode)
@Gtk.Template.Callback()
def on_delete_called(self, _widget, _data=None):
def on_close_request(self, _widget, _data=None):
"""Called when the ApostropheWindow is closed.
"""
LOGGER.info('delete called')
LOGGER.info('close request called')
response = self.check_change()
if (response == Gtk.ResponseType.CANCEL or
response == Gtk.ResponseType.DELETE_EVENT):
......
......@@ -20,8 +20,8 @@ import gi
from gettext import gettext as _
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gio, GLib, GObject, Handy
gi.require_version('Gtk', '4.0')
from gi.repository import Gtk, Gio, GLib, GObject, Adw
from .settings import Settings
class RecentItem(GObject.Object):
......@@ -57,19 +57,18 @@ class ApostropheOpenPopover(Gtk.Popover):
self.recents_manager.connect("changed", self.on_manager_changed)
def create_row(self, item, **args):
row = Handy.ActionRow.new()
row = Adw.ActionRow.new()
row.item = item
row.set_title(item.name)
row.set_subtitle(item.path)
delete_button = Gtk.Button.new_from_icon_name("window-close-symbolic", Gtk.IconSize.BUTTON)
delete_button = Gtk.Button.new_from_icon_name("window-close-symbolic")
delete_button.get_style_context().add_class("flat")
delete_button.get_style_context().add_class("circular")
delete_button.set_valign(Gtk.Align.CENTER)
delete_button.set_visible(True)
delete_button.connect("clicked", self.on_delete_click, item)
row.add(delete_button)
row.add_suffix(delete_button)
row.set_activatable(True)
row.set_action_name("win.open_file")
row.set_action_target_value(GLib.Variant.new_string(item.uri))
......
......@@ -19,7 +19,7 @@ import webbrowser
import gi