feat: add export dialog
Build / Flatpak (x86_64) (push) Failing after 1h11m8s Details

This commit is contained in:
0xMRTT 2023-08-03 14:33:54 +02:00
parent 8feb2281bc
commit 9812f28970
10 changed files with 252 additions and 5 deletions

View File

@ -2,7 +2,9 @@
<gresources>
<gresource prefix="/io/github/Bavarder/Bavarder">
<file preprocess="xml-stripblanks" alias="ui/window.ui">views/window.ui</file>
<file preprocess="xml-stripblanks" alias="ui/export_dialog.ui">views/export_dialog.ui</file>
<file preprocess="xml-stripblanks" alias="ui/preferences_window.ui">views/preferences_window.ui</file>
<file preprocess="xml-stripblanks" alias="ui/save_dialog.ui">views/save_dialog.ui</file>
<file preprocess="xml-stripblanks" alias="ui/thread_item.ui">widgets/thread_item.ui</file>
<file preprocess="xml-stripblanks" alias="ui/item.ui">widgets/item.ui</file>
<file preprocess="xml-stripblanks" alias="ui/provider_item.ui">providers/provider_item.ui</file>

View File

@ -5,7 +5,9 @@ python = import('python')
blueprints = custom_target('blueprints',
input: files(
'gtk/help-overlay.blp',
'views/export_dialog.blp',
'views/preferences_window.blp',
'views/save_dialog.blp',
'views/window.blp',
'widgets/thread_item.blp',
'widgets/item.blp',

View File

@ -0,0 +1,54 @@
using Gtk 4.0;
using Adw 1;
using GtkSource 5;
template $ExportDialog : Adw.MessageDialog {
heading: _("Export Thread ?");
response => $handle_response();
extra-child: Overlay {
[overlay]
Button {
styles [
"circular",
]
icon-name: "edit-copy-symbolic";
halign: end;
valign: end;
margin-bottom: 7;
margin-end: 7;
clicked => $copy();
}
Box box {
orientation: vertical;
vexpand: true;
hexpand: true;
Gtk.ScrolledWindow view {
vexpand: true;
hexpand: true;
min-content-height: 200;
GtkSource.View source_view {
vexpand: true;
hexpand: true;
buffer: GtkSource.Buffer buffer {};
editable: false;
monospace: true;
show-line-marks: false;
show-line-numbers: false;
smart-backspace: false;
margin-top: 5;
margin-bottom: 5;
styles [ "codeview", "card" ]
}
}
}
};
responses [
close: _("Close"),
export: _("Export") suggested,
]
}

View File

@ -0,0 +1,43 @@
from gi.repository import Gtk, Adw, Gio, GtkSource, Gdk
from bavarder.constants import app_id, rootdir
from bavarder.views.save_dialog import SaveDialog
GtkSource.init()
@Gtk.Template(resource_path=f"{rootdir}/ui/export_dialog.ui")
class ExportDialog(Adw.MessageDialog):
__gtype_name__ = "ExportDialog"
buffer = Gtk.Template.Child()
source_view = Gtk.Template.Child()
def __init__(self, parent, chat, **kwargs):
super().__init__(**kwargs)
self.text: str = ""
self.parent = parent
for i, x in zip(chat, range(len(chat))):
self.text += f"{i['role']}: {i['content']}\n"
if (x % 2) != 0:
self.text += "\n"
self.text = self.text[:-2]
self.buffer.set_text(self.text)
if Adw.StyleManager().get_dark():
self.buffer.set_style_scheme(GtkSource.StyleSchemeManager().get_scheme("Adwaita-dark"))
else:
self.buffer.set_style_scheme(GtkSource.StyleSchemeManager().get_scheme("Adwaita"))
@Gtk.Template.Callback()
def copy(self, *args, **kwargs):
Gdk.Display.get_default().get_clipboard().set(self.text)
@Gtk.Template.Callback()
def handle_response(self, dialog, response, *args, **kwargs):
if response == "export":
dialog = SaveDialog(self.parent, self.text)
dialog.present()

View File

@ -3,7 +3,9 @@ views_dir = join_paths(MODULE_DIR, 'views')
views_sources = [
'__init__.py',
'about_window.py',
'export_dialog.py',
'preferences_window.py',
'save_dialog.py',
'window.py',
]

View File

@ -95,13 +95,13 @@ class PreferencesWindow(Adw.PreferencesWindow):
@Gtk.Template.Callback()
def on_bot_entry_apply(self, user_data, *args):
self.app.bot_name = user_data.get_text().capitalize()
self.app.bot_name = user_data.get_text()
self.app.load_bot_and_user_name()
@Gtk.Template.Callback()
def on_user_entry_apply(self, user_data, *args):
self.app.user_name = user_data.get_text().capitalize()
self.app.user_name = user_data.get_text()
self.app.load_bot_and_user_name()

69
src/views/save_dialog.blp Normal file
View File

@ -0,0 +1,69 @@
using Gtk 4.0;
using Adw 1;
template $SaveDialog : Adw.MessageDialog {
response => $handle_response();
responses [
cancel: _("Cancel"),
disacard: _("Discard") destructive,
save: _("Save") suggested disabled,
]
close-response: "cancel";
modal: true;
heading: _("Export Thread?");
body: _("");
extra-child: Box {
margin-top: 12;
orientation: vertical;
spacing: 24;
ListBox {
selection-mode: none;
styles ["boxed-list"]
Adw.EntryRow filename {
title: _("File Name");
entry-activated => $on_entry_activated();
}
}
Box {
orientation: vertical;
ListBox {
selection-mode: none;
styles ["boxed-list"]
Adw.ActionRow location {
title: _("Location");
subtitle: "Select Location";
activatable-widget: button_location;
Button button_location {
icon-name: "folder-symbolic";
valign: center;
styles ["flat"]
clicked => $on_location_button_clicked();
}
}
}
Label {
margin-start: 12;
margin-top: 12;
halign: start;
label: _("The export of the Thread will be saved in this directory.");
styles ["dim-label", "caption"]
justify: left;
}
}
};
}
Gtk.FileDialog file_chooser {
title: _("Choose a directory");
modal: true;
//action: open;
//response => $on_filechooser_response();
}

57
src/views/save_dialog.py Normal file
View File

@ -0,0 +1,57 @@
from gi.repository import Gtk, Adw, Gio, Gdk
from bavarder.constants import app_id, rootdir
@Gtk.Template(resource_path=f"{rootdir}/ui/save_dialog.ui")
class SaveDialog(Adw.MessageDialog):
__gtype_name__ = "SaveDialog"
filename = Gtk.Template.Child()
file_chooser = Gtk.Template.Child()
location = Gtk.Template.Child()
def __init__(self, parent, text, **kwargs):
super().__init__(**kwargs)
self.text: str = text
self.parent = parent
@Gtk.Template.Callback()
def handle_response(self, dialog, response, *args, **kwargs):
if response == "save":
filename = self.filename.get_text()
path = f"{self.directory}/{filename}.md"
toast = Adw.Toast()
try:
with open(path, "w") as f:
f.write(self.text)
except FileNotFoundError:
toast.set_title(_("Unable to save the Thread"))
else:
toast.set_title(_("Thread successfully saved!"))
self.parent.toast_overlay.add_toast(toast)
@Gtk.Template.Callback()
def on_location_button_clicked(self, widget, *args):
self.file_chooser.select_folder(self, None, self.on_filechooser_response)
def on_filechooser_response(self, widget, response):
self.directory = self.file_chooser.select_folder_finish(response).get_path()
self.location.set_subtitle(self.directory)
self.update_save_status()
@Gtk.Template.Callback()
def on_entry_activated(self, widget, *args):
self.update_save_status()
def update_save_status(self):
try:
self.directory
except Exception:
self.set_response_enabled("save", False)
else:
if self.filename.get_text().strip():
self.set_response_enabled("save", True)
else:
self.set_response_enabled("save", False)

View File

@ -28,6 +28,7 @@ from bavarder.constants import app_id, build_type, rootdir
from bavarder.widgets.thread_item import ThreadItem
from bavarder.widgets.item import Item
from bavarder.threading import KillableThread
from bavarder.views.export_dialog import ExportDialog
class CustomEntry(Gtk.TextView):
def __init__(self, **kwargs):
@ -85,6 +86,7 @@ class BavarderWindow(Adw.ApplicationWindow):
self.create_action("cancel", self.cancel, ["<primary>Escape"])
self.create_action("clear_all", self.on_clear_all)
self.create_action("export", self.on_export, ["<primary>e"])
self.settings.bind(
"width", self, "default-width", Gio.SettingsBindFlags.DEFAULT
@ -202,6 +204,12 @@ class BavarderWindow(Adw.ApplicationWindow):
del self.chat["content"]
self.stack.set_visible_child(self.status_no_chat)
def on_export(self, *args):
if self.content:
dialog = ExportDialog(self, self.chat["content"])
dialog.set_transient_for(self)
dialog.present()
# PROVIDER - ONLINE
def load_provider_selector(self):
provider_menu = Gio.Menu()
@ -229,6 +237,11 @@ class BavarderWindow(Adw.ApplicationWindow):
item_provider.set_action_and_target_value("win.clear_all", None)
section.append_item(item_provider)
item_provider = Gio.MenuItem()
item_provider.set_label(_("Export"))
item_provider.set_action_and_target_value("win.export", None)
section.append_item(item_provider)
provider_menu.append_section(None, section)
self.provider_selector_button.set_menu_model(provider_menu)
@ -262,6 +275,11 @@ class BavarderWindow(Adw.ApplicationWindow):
item_provider.set_action_and_target_value("win.clear_all", None)
section.append_item(item_provider)
item_provider = Gio.MenuItem()
item_provider.set_label(_("Export"))
item_provider.set_action_and_target_value("win.export", None)
section.append_item(item_provider)
provider_menu.append_section(None, section)
self.model_selector_button.set_menu_model(provider_menu)

View File

@ -109,7 +109,7 @@ class Item(Gtk.Box):
label.set_halign(Gtk.Align.START)
self.content.append(label)
t = self.item["role"].capitalize()
t = self.item["role"].lower()
self.parent = parent
self.settings = parent.settings
@ -117,11 +117,11 @@ class Item(Gtk.Box):
self.app = self.parent.get_application()
self.win = self.app.get_active_window()
if t == self.app.user_name: # User
if t == self.app.user_name.lower(): # User
self.message_bubble.add_css_class("message-bubble-user")
self.avatar.add_css_class("avatar-user")
role = self.app.user_name
elif t == self.app.bot_name: # Assistant
elif t == self.app.bot_name.lower(): # Assistant
self.avatar.set_icon_name("bot-symbolic")
self.user.add_css_class("warning")
role = self.app.bot_name