Initial commit
The only thing working is showing the users calendars in the sidebar.
This commit is contained in:
commit
b07ce820cd
30 changed files with 2314 additions and 0 deletions
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
85
src/gsettings.py
Normal file
85
src/gsettings.py
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Copyright 2023 Vlad Krupinskii <mrvladus@yandex.ru>
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from gi.repository import GLib, Gio, Gtk
|
||||
import gi
|
||||
gi.require_version('Secret', '1')
|
||||
from gi.repository import Secret
|
||||
|
||||
APP_ID = "net.jeena.jnotes"
|
||||
SECRETS_SCHEMA = Secret.Schema.new(
|
||||
APP_ID,
|
||||
Secret.SchemaFlags.NONE,
|
||||
{
|
||||
"account": Secret.SchemaAttributeType.STRING,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class GSettings:
|
||||
"""Class for accessing gsettings"""
|
||||
|
||||
gsettings: Gio.Settings = None
|
||||
initialized: bool = False
|
||||
|
||||
def _check_init(self):
|
||||
if not self.initialized:
|
||||
self.init()
|
||||
|
||||
@classmethod
|
||||
def bind(
|
||||
self, setting: str, obj: Gtk.Widget, prop: str, invert: bool = False
|
||||
) -> None:
|
||||
self._check_init(self)
|
||||
self.gsettings.bind(
|
||||
setting,
|
||||
obj,
|
||||
prop,
|
||||
Gio.SettingsBindFlags.INVERT_BOOLEAN
|
||||
if invert
|
||||
else Gio.SettingsBindFlags.DEFAULT,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def get(self, setting: str):
|
||||
self._check_init(self)
|
||||
return self.gsettings.get_value(setting).unpack()
|
||||
|
||||
@classmethod
|
||||
def set(self, setting: str, gvariant: str, value) -> None:
|
||||
self._check_init(self)
|
||||
self.gsettings.set_value(setting, GLib.Variant(gvariant, value))
|
||||
|
||||
@classmethod
|
||||
def get_secret(self, account: str):
|
||||
self._check_init(self)
|
||||
return Secret.password_lookup_sync(SECRETS_SCHEMA, {"account": account}, None)
|
||||
|
||||
@classmethod
|
||||
def set_secret(self, account: str, secret: str) -> None:
|
||||
self._check_init(self)
|
||||
|
||||
return Secret.password_store_sync(
|
||||
SECRETS_SCHEMA,
|
||||
{
|
||||
"account": account,
|
||||
},
|
||||
Secret.COLLECTION_DEFAULT,
|
||||
f"Errands account credentials for {account}",
|
||||
secret,
|
||||
None,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def init(self) -> None:
|
||||
self.initialized = True
|
||||
self.gsettings = Gio.Settings.new(APP_ID)
|
||||
|
||||
# Migrate old password
|
||||
try:
|
||||
password = self.gsettings.get_string("password")
|
||||
if password:
|
||||
self.set_secret("CalDAV", password)
|
||||
self.gsettings.set_string("password", "") # Clean pass
|
||||
except:
|
||||
pass
|
||||
9
src/jnotes.gresource.xml
Normal file
9
src/jnotes.gresource.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/net/jeena/jnotes">
|
||||
<file preprocess="xml-stripblanks">ui/window.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/preferences.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/help-overlay.ui</file>
|
||||
<file preprocess="xml-stripblanks">ui/sidebar.ui</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
46
src/jnotes.in
Executable file
46
src/jnotes.in
Executable file
|
|
@ -0,0 +1,46 @@
|
|||
#!@PYTHON@
|
||||
|
||||
# jnotes.in
|
||||
#
|
||||
# Copyright 2024 Jeena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import sys
|
||||
import signal
|
||||
import locale
|
||||
import gettext
|
||||
|
||||
VERSION = '@VERSION@'
|
||||
pkgdatadir = '@pkgdatadir@'
|
||||
localedir = '@localedir@'
|
||||
|
||||
sys.path.insert(1, pkgdatadir)
|
||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||
locale.bindtextdomain('jnotes', localedir)
|
||||
locale.textdomain('jnotes')
|
||||
gettext.install('jnotes', localedir)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import gi
|
||||
|
||||
from gi.repository import Gio
|
||||
resource = Gio.Resource.load(os.path.join(pkgdatadir, 'jnotes.gresource'))
|
||||
resource._register()
|
||||
|
||||
from jnotes import main
|
||||
sys.exit(main.main(VERSION))
|
||||
97
src/main.py
Normal file
97
src/main.py
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# main.py
|
||||
#
|
||||
# Copyright 2024 Jeena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import sys
|
||||
import gi
|
||||
|
||||
gi.require_version('Gtk', '4.0')
|
||||
gi.require_version('Adw', '1')
|
||||
|
||||
from gi.repository import Gtk, Gio, Adw
|
||||
from .window import JnotesWindow
|
||||
from .preferences import PreferencesWindow
|
||||
from .sidebar import Sidebar
|
||||
from .sync import Sync
|
||||
|
||||
|
||||
class JnotesApplication(Adw.Application):
|
||||
"""The main application singleton class."""
|
||||
|
||||
calendar_set = None
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(application_id='net.jeena.jnotes',
|
||||
flags=Gio.ApplicationFlags.DEFAULT_FLAGS)
|
||||
self.create_action('quit', lambda *_: self.quit(), ['<primary>q'])
|
||||
self.create_action('about', self.on_about_action)
|
||||
self.create_action('preferences', self.on_preferences_action)
|
||||
|
||||
def do_activate(self):
|
||||
"""Called when the application is activated.
|
||||
|
||||
We raise the application's main window, creating it if
|
||||
necessary.
|
||||
"""
|
||||
win = self.props.active_window
|
||||
if not win:
|
||||
win = JnotesWindow(application=self)
|
||||
win.present()
|
||||
|
||||
self.calendar_set = Sync.get_calendar_set()
|
||||
if not self.calendar_set:
|
||||
self.on_preferences_action(win, False)
|
||||
else:
|
||||
win.sidebar.set_calendars(self.calendar_set)
|
||||
|
||||
def on_about_action(self, widget, _):
|
||||
"""Callback for the app.about action."""
|
||||
about = Adw.AboutWindow(transient_for=self.props.active_window,
|
||||
application_name='jnotes',
|
||||
application_icon='net.jeena.jnotes',
|
||||
developer_name='Jeena',
|
||||
version='0.1.0',
|
||||
developers=['Jeena'],
|
||||
copyright='© 2024 Jeena')
|
||||
about.present()
|
||||
|
||||
def on_preferences_action(self, widget, _):
|
||||
"""Callback for the app.preferences action."""
|
||||
preferences_window = PreferencesWindow(transient_for=self.props.active_window)
|
||||
preferences_window.present()
|
||||
|
||||
def create_action(self, name, callback, shortcuts=None):
|
||||
"""Add an application action.
|
||||
|
||||
Args:
|
||||
name: the name of the action
|
||||
callback: the function to be called when the action is
|
||||
activated
|
||||
shortcuts: an optional list of accelerators
|
||||
"""
|
||||
action = Gio.SimpleAction.new(name, None)
|
||||
action.connect("activate", callback)
|
||||
self.add_action(action)
|
||||
if shortcuts:
|
||||
self.set_accels_for_action(f"app.{name}", shortcuts)
|
||||
|
||||
|
||||
def main(version):
|
||||
"""The application's entry point."""
|
||||
app = JnotesApplication()
|
||||
return app.run(sys.argv)
|
||||
39
src/meson.build
Normal file
39
src/meson.build
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
pkgdatadir = get_option('prefix') / get_option('datadir') / meson.project_name()
|
||||
moduledir = pkgdatadir / 'jnotes'
|
||||
gnome = import('gnome')
|
||||
|
||||
gnome.compile_resources('jnotes',
|
||||
'jnotes.gresource.xml',
|
||||
gresource_bundle: true,
|
||||
install: true,
|
||||
install_dir: pkgdatadir,
|
||||
)
|
||||
|
||||
python = import('python')
|
||||
|
||||
conf = configuration_data()
|
||||
conf.set('PYTHON', python.find_installation('python3').path())
|
||||
conf.set('VERSION', meson.project_version())
|
||||
conf.set('localedir', get_option('prefix') / get_option('localedir'))
|
||||
conf.set('pkgdatadir', pkgdatadir)
|
||||
|
||||
configure_file(
|
||||
input: 'jnotes.in',
|
||||
output: 'jnotes',
|
||||
configuration: conf,
|
||||
install: true,
|
||||
install_dir: get_option('bindir'),
|
||||
install_mode: 'r-xr--r--'
|
||||
)
|
||||
|
||||
jnotes_sources = [
|
||||
'__init__.py',
|
||||
'main.py',
|
||||
'window.py',
|
||||
'preferences.py',
|
||||
'gsettings.py',
|
||||
'sync.py',
|
||||
'sidebar.py',
|
||||
]
|
||||
|
||||
install_data(jnotes_sources, install_dir: moduledir)
|
||||
80
src/preferences.py
Normal file
80
src/preferences.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# preferences.py
|
||||
#
|
||||
# Copyright 2024 Jeena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from gi.repository import Adw
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import GLib
|
||||
from .gsettings import GSettings
|
||||
from urllib.parse import urlparse
|
||||
from .sync import Sync
|
||||
|
||||
@Gtk.Template(resource_path='/net/jeena/jnotes/ui/preferences.ui')
|
||||
class PreferencesWindow(Adw.PreferencesWindow):
|
||||
__gtype_name__ = 'PreferencesWindow'
|
||||
|
||||
server_url = Gtk.Template.Child()
|
||||
username = Gtk.Template.Child()
|
||||
password = Gtk.Template.Child()
|
||||
spinner = Gtk.Template.Child()
|
||||
test_connection_row = Gtk.Template.Child()
|
||||
test_connection = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
GSettings.bind("server-url", self.server_url, "text")
|
||||
GSettings.bind("username", self.username, "text")
|
||||
self.password.set_text(GSettings.get_secret("CalDAV"))
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_test_connection_button_clicked(self, _btn):
|
||||
server_url = self.server_url.get_text()
|
||||
username = self.username.get_text()
|
||||
password = self.password.get_text()
|
||||
GLib.idle_add(self.spinner.start)
|
||||
GLib.idle_add(self.deactivate_test_button)
|
||||
Sync.test_connection(server_url, username, password, self.sync_ok_callback)
|
||||
|
||||
@Gtk.Template.Callback()
|
||||
def on_validate(self, _lbl):
|
||||
server_url = urlparse(self.server_url.get_text())
|
||||
server_url_ok = bool(server_url.scheme and server_url.hostname)
|
||||
username_ok = len(self.username.get_text()) > 0
|
||||
password_ok = len(self.password.get_text()) > 0
|
||||
|
||||
if (server_url_ok and username_ok and password_ok):
|
||||
self.activate_test_button()
|
||||
else:
|
||||
self.deactivate_test_button()
|
||||
|
||||
def activate_test_button(self):
|
||||
self.test_connection.add_css_class("suggested-action")
|
||||
self.test_connection.set_sensitive(True)
|
||||
|
||||
def deactivate_test_button(self):
|
||||
self.test_connection.remove_css_class("suggested-action")
|
||||
self.test_connection.set_sensitive(False)
|
||||
|
||||
def sync_ok_callback(self, ok, e=None):
|
||||
self.activate_test_button()
|
||||
self.spinner.stop()
|
||||
if ok:
|
||||
self.test_connection_row.set_icon_name("emblem-ok-symbolic")
|
||||
else:
|
||||
self.test_connection_row.set_icon_name("network-error-symbolic")
|
||||
|
||||
38
src/sidebar.py
Normal file
38
src/sidebar.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# sidebar.py
|
||||
#
|
||||
# Copyright 2024 Jeena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from gi.repository import Adw
|
||||
from gi.repository import Gtk
|
||||
|
||||
@Gtk.Template(resource_path='/net/jeena/jnotes/ui/sidebar.ui')
|
||||
class Sidebar(Adw.NavigationPage):
|
||||
__gtype_name__ = 'Sidebar'
|
||||
|
||||
calendar_set = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def set_calendars(self, calendars):
|
||||
self.calendar_set.bind_model(calendars, self.create_item_widget)
|
||||
|
||||
def create_item_widget(self, calendar):
|
||||
return Gtk.Label(label=calendar.displayname, halign="start",
|
||||
hexpand=True, ellipsize=3)
|
||||
|
||||
119
src/sync.py
Normal file
119
src/sync.py
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# sync.py
|
||||
#
|
||||
# Copyright 2024 Jeena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import caldav
|
||||
import logging, requests
|
||||
from gi.repository import Gtk, Gio, GObject
|
||||
from threading import Thread
|
||||
from typing import Callable
|
||||
from .gsettings import GSettings
|
||||
|
||||
def threaded(function: Callable):
|
||||
"""
|
||||
Decorator to run function in thread.
|
||||
Use GLib.idle_add(func) as callback at the end of threaded function
|
||||
if you need to change UI from thread.
|
||||
It's needed to be called to make changes in UI thread safe.
|
||||
"""
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
Thread(target=function, args=args, kwargs=kwargs, daemon=True).start()
|
||||
|
||||
return wrapper
|
||||
|
||||
class Sync():
|
||||
client = None
|
||||
principal = None
|
||||
|
||||
@classmethod
|
||||
@threaded
|
||||
def test_connection(self, server_url, username, password, callback):
|
||||
try:
|
||||
client = caldav.DAVClient(server_url, username=username, password=password)
|
||||
client.principal()
|
||||
callback(True)
|
||||
except caldav.lib.error.AuthorizationError as e:
|
||||
logging.warning(e)
|
||||
callback(False, e)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
logging.warning(e)
|
||||
callback(False, e)
|
||||
|
||||
@classmethod
|
||||
def init(self):
|
||||
server_url = GSettings.get("server-url")
|
||||
username = GSettings.get("username")
|
||||
password = GSettings.get_secret("CalDAV")
|
||||
|
||||
try:
|
||||
self.client = caldav.DAVClient(server_url, username=username, password=password)
|
||||
self.principal = self.client.principal()
|
||||
except caldav.lib.error.AuthorizationError as e:
|
||||
logging.warning(e)
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
logging.warning(e)
|
||||
|
||||
@classmethod
|
||||
def get_calendar_set(self):
|
||||
calendar_set = CalendarSet()
|
||||
if not self.client:
|
||||
self.init()
|
||||
if self.principal:
|
||||
remote_calendars = self.principal.calendars()
|
||||
for remote_calendar in remote_calendars:
|
||||
if "VJOURNAL" in remote_calendar.get_supported_components():
|
||||
calendar = Calendar(remote_calendar.get_display_name(), remote_calendar.url)
|
||||
for journal in remote_calendar.journals():
|
||||
summary = journal.icalendar_component.get("summary", "")
|
||||
description = journal.icalendar_component.get("description", "")
|
||||
calendar.add_note(Note(calendar, summary, description))
|
||||
calendar_set.add_calendar(calendar)
|
||||
|
||||
return calendar_set
|
||||
|
||||
class CalendarSet(Gio.ListStore):
|
||||
def add_calendar(self, calendar):
|
||||
self.append(calendar)
|
||||
|
||||
def remove_calendar(self, calendar):
|
||||
self.remove(calendar)
|
||||
|
||||
class Calendar(Gio.ListStore):
|
||||
displayname = None
|
||||
cal_id = None
|
||||
|
||||
def __init__(self, displayname, cal_id):
|
||||
super().__init__()
|
||||
self.displayname = displayname
|
||||
self.cal_id = cal_id
|
||||
|
||||
def add_note(self, note):
|
||||
self.append(note)
|
||||
|
||||
class Note(GObject.GObject):
|
||||
uid = None
|
||||
calendar = None
|
||||
summary = None
|
||||
description = None
|
||||
|
||||
def __init__(self, calendar, summary, description):
|
||||
super().__init__()
|
||||
self.calendar = calendar
|
||||
self.summary = summary
|
||||
self.description = description
|
||||
29
src/ui/help-overlay.ui
Normal file
29
src/ui/help-overlay.ui
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<object class="GtkShortcutsWindow" id="help_overlay">
|
||||
<property name="modal">True</property>
|
||||
<child>
|
||||
<object class="GtkShortcutsSection">
|
||||
<property name="section-name">shortcuts</property>
|
||||
<property name="max-height">10</property>
|
||||
<child>
|
||||
<object class="GtkShortcutsGroup">
|
||||
<property name="title" translatable="yes" context="shortcut window">General</property>
|
||||
<child>
|
||||
<object class="GtkShortcutsShortcut">
|
||||
<property name="title" translatable="yes" context="shortcut window">Show Shortcuts</property>
|
||||
<property name="action-name">win.show-help-overlay</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkShortcutsShortcut">
|
||||
<property name="title" translatable="yes" context="shortcut window">Quit</property>
|
||||
<property name="action-name">app.quit</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</interface>
|
||||
54
src/ui/preferences.ui
Normal file
54
src/ui/preferences.ui
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0" />
|
||||
<template class="PreferencesWindow" parent="AdwPreferencesWindow">
|
||||
<property name="search_enabled">False</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesPage">
|
||||
<property name="name" translatable="yes">Sync</property>
|
||||
<property name="title" translatable="yes">Sync information</property>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup">
|
||||
<property name="name" translatable="yes">CalDav</property>
|
||||
<property name="title" translatable="yes">CalDav Sync Server</property>
|
||||
<child>
|
||||
<object class="AdwEntryRow" id="server_url">
|
||||
<property name="title" translatable="yes">Server URL</property>
|
||||
<signal name="changed" handler="on_validate" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwEntryRow" id="username">
|
||||
<property name="title" translatable="yes">Username</property>
|
||||
<signal name="changed" handler="on_validate" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPasswordEntryRow" id="password">
|
||||
<property name="title" translatable="yes">Password</property>
|
||||
<signal name="changed" handler="on_validate" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow" id="test_connection_row">
|
||||
<property name="title">Test Connection</property>
|
||||
<child>
|
||||
<object class="GtkSpinner" id="spinner"></object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="test_connection">
|
||||
<property name="label" translatable="yes">_Test</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<signal name="clicked" handler="on_test_connection_button_clicked" swapped="no" />
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
34
src/ui/sidebar.ui
Normal file
34
src/ui/sidebar.ui
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk" version="4.0"/>
|
||||
<template class="Sidebar" parent="AdwNavigationPage">
|
||||
<property name="title">Notes</property>
|
||||
<property name="child">
|
||||
<object class="AdwToolbarView">
|
||||
<child type="top">
|
||||
<object class="AdwHeaderBar">
|
||||
<child type="start">
|
||||
<object class="GtkToggleButton">
|
||||
<property name="icon-name">list-add-symbolic</property>
|
||||
<property name="tooltip-text" translatable="yes">New Collection</property>
|
||||
<property name="action-name">win.new-collection</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<property name="content">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="child">
|
||||
<object class="GtkListBox" id="calendar_set">
|
||||
<style>
|
||||
<class name="navigation-sidebar"/>
|
||||
</style>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
||||
101
src/ui/window.ui
Normal file
101
src/ui/window.ui
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<menu id="main-menu">
|
||||
<section>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Preferences</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
<attribute name="accel"><Primary>comma</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Keyboard Shortcuts</attribute>
|
||||
<attribute name="action">win.show-help-overlay</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_About</attribute>
|
||||
<attribute name="action">app.about</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Quit</attribute>
|
||||
<attribute name="action">app.quit</attribute>
|
||||
<attribute name="accel"><Primary>q</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
<template class="JnotesWindow" parent="AdwApplicationWindow">
|
||||
<property name="title" translatable="yes">To-Do</property>
|
||||
<property name="width-request">800</property>
|
||||
<property name="height-request">800</property>
|
||||
<child>
|
||||
<object class="AdwBreakpoint">
|
||||
<condition>max-width: 500sp</condition>
|
||||
<setter object="split_view" property="collapsed">True</setter>
|
||||
</object>
|
||||
</child>
|
||||
<property name="content">
|
||||
<object class="AdwNavigationSplitView" id="split_view">
|
||||
<property name="min-sidebar-width">200</property>
|
||||
<property name="sidebar">
|
||||
<object class="Sidebar" id="sidebar">
|
||||
|
||||
</object>
|
||||
</property>
|
||||
<property name="content">
|
||||
<object class="AdwNavigationPage">
|
||||
<property name="title" translatable="yes">Note</property>
|
||||
<property name="child">
|
||||
<object class="AdwToolbarView">
|
||||
<child type="top">
|
||||
<object class="AdwHeaderBar">
|
||||
<property name="show-title">False</property>
|
||||
<child type="end">
|
||||
<object class="GtkMenuButton">
|
||||
<property name="icon-name">open-menu-symbolic</property>
|
||||
<property name="menu-model">main-menu</property>
|
||||
<property name="tooltip-text" translatable="yes">Main Menu</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<property name="content">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="child">
|
||||
<object class="AdwClamp">
|
||||
<property name="maximum-size">400</property>
|
||||
<property name="tightening-threshold">300</property>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin-start">12</property>
|
||||
<property name="margin-end">12</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="entry">
|
||||
<property name="placeholder-text" translatable="yes">Create new Note…</property>
|
||||
<property name="secondary-icon-name">list-add-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBox" id="tasks_list">
|
||||
<property name="visible">False</property>
|
||||
<property name="selection-mode">none</property>
|
||||
<style>
|
||||
<class name="boxed-list" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
||||
31
src/window.py
Normal file
31
src/window.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# window.py
|
||||
#
|
||||
# Copyright 2024 Jeena
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from gi.repository import Adw
|
||||
from gi.repository import Gtk
|
||||
|
||||
@Gtk.Template(resource_path='/net/jeena/jnotes/ui/window.ui')
|
||||
class JnotesWindow(Adw.ApplicationWindow):
|
||||
__gtype_name__ = 'JnotesWindow'
|
||||
|
||||
sidebar = Gtk.Template.Child()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue