Browse Source

session-view: Port to Blueprint

fractal-13
Kévin Commaille 7 months ago
parent
commit
9d6817b9ad
No known key found for this signature in database
GPG Key ID: F26F4BE20A08255B
  1. 8
      po/POTFILES.in
  2. 5
      src/blp-resources.in
  3. 105
      src/session/view/create_direct_chat_dialog/mod.blp
  4. 123
      src/session/view/create_direct_chat_dialog/mod.ui
  5. 173
      src/session/view/create_room_dialog.blp
  6. 186
      src/session/view/create_room_dialog.ui
  7. 202
      src/session/view/event_details_dialog.blp
  8. 266
      src/session/view/event_details_dialog.ui
  9. 141
      src/session/view/media_viewer.blp
  10. 141
      src/session/view/media_viewer.ui
  11. 54
      src/session/view/session_view.blp
  12. 82
      src/session/view/session_view.ui
  13. 5
      src/ui-resources.gresource.xml

8
po/POTFILES.in

@ -186,13 +186,13 @@ src/session/view/content/room_history/title.blp
src/session/view/content/room_history/typing_row.rs
src/session/view/content/room_history/verification_info_bar.rs
src/session/view/create_direct_chat_dialog/mod.rs
src/session/view/create_direct_chat_dialog/mod.ui
src/session/view/create_direct_chat_dialog/mod.blp
src/session/view/create_room_dialog.rs
src/session/view/create_room_dialog.ui
src/session/view/create_room_dialog.blp
src/session/view/event_details_dialog.rs
src/session/view/event_details_dialog.ui
src/session/view/event_details_dialog.blp
src/session/view/media_viewer.rs
src/session/view/media_viewer.ui
src/session/view/media_viewer.blp
src/session/view/sidebar/mod.rs
src/session/view/sidebar/mod.ui
src/session/view/sidebar/room_row.rs

5
src/blp-resources.in

@ -134,4 +134,9 @@ session/view/content/room_history/state/row.blp
session/view/content/room_history/title.blp
session/view/content/room_history/typing_row.blp
session/view/content/room_history/verification_info_bar.blp
session/view/create_direct_chat_dialog/mod.blp
session/view/create_room_dialog.blp
session/view/event_details_dialog.blp
session/view/media_viewer.blp
session/view/session_view.blp
window.blp

105
src/session/view/create_direct_chat_dialog/mod.blp

@ -0,0 +1,105 @@
using Gtk 4.0;
using Adw 1;
template $CreateDirectChatDialog: Adw.Dialog {
title: _("Direct Chat");
content-width: 380;
content-height: 620;
child: Adw.ToolbarView {
[top]
Adw.HeaderBar {
show-title: false;
}
[top]
Adw.Clamp {
hexpand: true;
Gtk.Box {
orientation: vertical;
spacing: 18;
margin-start: 12;
margin-end: 12;
Gtk.Label heading {
wrap: true;
wrap-mode: word_char;
max-width-chars: 20;
justify: center;
xalign: 0.5;
label: _("New Direct Chat");
styles [
"title-2",
]
}
Gtk.SearchEntry search_entry {}
}
}
content: Gtk.Stack stack {
Gtk.StackPage {
name: "no-search-term";
child: Adw.StatusPage {
vexpand: true;
icon-name: "system-search-symbolic";
// Translators: In this string, 'Search' is a verb.
title: _("Search");
description: _("Search for people to start a new chat with");
};
}
Gtk.StackPage {
name: "results";
child: Gtk.ScrolledWindow matching_page {
styles [
"avatar-row-list",
]
child: Adw.Clamp {
child: Gtk.ListBox list_box {
activate-on-single-click: true;
margin-start: 6;
margin-end: 6;
row-activated => $create_direct_chat() swapped;
styles [
"navigation-sidebar",
]
};
};
};
}
Gtk.StackPage {
name: "empty";
child: Adw.StatusPage {
icon-name: "system-search-symbolic";
title: _("No Users Found");
description: _("No users matching the search pattern were found");
};
}
Gtk.StackPage {
name: "error";
child: Adw.StatusPage error_page {
icon-name: "error-symbolic";
title: _("Error");
description: _("An error occurred while searching for matches");
};
}
Gtk.StackPage {
name: "loading";
child: Adw.Spinner {};
}
};
};
}

123
src/session/view/create_direct_chat_dialog/mod.ui

@ -1,123 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="CreateDirectChatDialog" parent="AdwDialog">
<property name="title" translatable="yes">Direct Chat</property>
<property name="content-width">380</property>
<property name="content-height">620</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar">
<property name="show-title">False</property>
</object>
</child>
<child type="top">
<object class="AdwClamp">
<property name="hexpand">True</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<child>
<object class="GtkLabel" id="heading">
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="max-width-chars">20</property>
<property name="justify">center</property>
<property name="xalign">0.5</property>
<property name="label" translatable="yes">New Direct Chat</property>
<style>
<class name="title-2"/>
</style>
</object>
</child>
<child>
<object class="GtkSearchEntry" id="search_entry"/>
</child>
</object>
</child>
</object>
</child>
<property name="content">
<object class="GtkStack" id="stack">
<child>
<object class="GtkStackPage">
<property name="name">no-search-term</property>
<property name="child">
<object class="AdwStatusPage">
<property name="vexpand">True</property>
<property name="icon-name">system-search-symbolic</property>
<!-- Translators: In this string, this is a verb. -->
<property name="title" translatable="yes">Search</property>
<property name="description" translatable="yes">Search for people to start a new chat with</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">results</property>
<property name="child">
<object class="GtkScrolledWindow" id="matching_page">
<style>
<class name="avatar-row-list"/>
</style>
<property name="child">
<object class="AdwClamp">
<property name="child">
<object class="GtkListBox" id="list_box">
<property name="activate-on-single-click">True</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<signal name="row-activated" handler="create_direct_chat" swapped="yes"/>
<style>
<class name="navigation-sidebar"/>
</style>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">empty</property>
<property name="child">
<object class="AdwStatusPage">
<property name="icon-name">system-search-symbolic</property>
<property name="title" translatable="yes">No Users Found</property>
<property name="description" translatable="yes">No users matching the search pattern were found</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">error</property>
<property name="child">
<object class="AdwStatusPage" id="error_page">
<property name="icon-name">error-symbolic</property>
<property name="title" translatable="yes">Error</property>
<property name="description" translatable="yes">An error occurred while searching for matches</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkStackPage">
<property name="name">loading</property>
<property name="child">
<object class="AdwSpinner" />
</property>
</object>
</child>
</object>
</property>
</object>
</property>
</template>
</interface>

173
src/session/view/create_room_dialog.blp

@ -0,0 +1,173 @@
using Gtk 4.0;
using Adw 1;
template $CreateRoomDialog: $ToastableDialog {
default-widget: create_button;
content-width: 380;
child-content: Adw.ToolbarView {
[top]
Adw.HeaderBar {
show-title: false;
}
content: Gtk.ScrolledWindow scrolled_window {
hscrollbar-policy: never;
propagate-natural-height: true;
vexpand: true;
child: Adw.Clamp {
maximum-size: 444;
child: Gtk.Box {
orientation: vertical;
spacing: 24;
margin-bottom: 24;
margin-start: 24;
margin-end: 24;
[top]
Gtk.Label heading {
wrap: true;
wrap-mode: word_char;
max-width-chars: 20;
justify: center;
xalign: 0.5;
label: _("New Room");
styles [
"title-2",
]
}
Gtk.Box content {
orientation: vertical;
spacing: 18;
Adw.PreferencesGroup {
title: _("Name");
Adw.EntryRow room_name {
title: _("Name");
changed => $validate_form() swapped;
entry-activated => $create_room() swapped;
}
}
Adw.PreferencesGroup topic_group {
title: _("Description (Optional)");
Gtk.ScrolledWindow {
height-request: 120;
styles [
"card",
]
Gtk.TextView topic_text_view {
hexpand: true;
wrap-mode: word_char;
styles [
"inline",
]
accessibility {
labelled-by: [
topic_group,
];
}
}
}
}
Adw.PreferencesGroup {
title: _("Room Visibility");
Adw.ActionRow {
title: _("Private");
subtitle: _("Only invited people can join this room");
activatable-widget: visibility_private;
[prefix]
Gtk.CheckButton visibility_private {
valign: center;
active: true;
toggled => $validate_form() swapped;
}
}
Adw.ActionRow {
title: _("Public");
subtitle: _("Anyone can find and join this room");
activatable-widget: visibility_public;
[prefix]
Gtk.CheckButton visibility_public {
valign: center;
group: visibility_private;
}
}
}
Adw.PreferencesGroup {
visible: bind visibility_private.active;
margin-top: 12;
Adw.SwitchRow encryption {
title: _("End-to-End Encryption");
subtitle: _("Cannot be disabled later");
}
}
Adw.PreferencesGroup {
visible: bind visibility_public.active;
margin-top: 12;
$SubstringEntryRow room_address {
title: _("Main Address");
// Translators: This is the placeholder for the first part of a room address,
// as in '@my-room:example.org'.
placeholder-text: _("my-room");
accessible-description: _("First part of the address, for example “my-room”");
prefix-text: "#";
hide-add-button: true;
changed => $validate_form() swapped;
}
Gtk.Revealer room_address_error_revealer {
child: Gtk.Label room_address_error {
wrap: true;
wrap-mode: word_char;
xalign: 0.0;
margin-top: 6;
styles [
"error",
"caption",
]
};
}
}
}
$LoadingButton create_button {
content-label: _("_Create Room");
use-underline: true;
sensitive: false;
halign: center;
valign: end;
vexpand: true;
clicked => $create_room() swapped;
styles [
"suggested-action",
"standalone-button",
"pill",
]
}
};
};
};
};
}

186
src/session/view/create_room_dialog.ui

@ -1,186 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="CreateRoomDialog" parent="ToastableDialog">
<property name="default-widget">create_button</property>
<property name="content-width">380</property>
<property name="child-content">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar">
<property name="show-title">False</property>
</object>
</child>
<property name="content">
<object class="GtkScrolledWindow" id="scrolled_window">
<property name="hscrollbar-policy">never</property>
<property name="propagate-natural-height">True</property>
<property name="vexpand">True</property>
<property name="child">
<object class="AdwClamp">
<property name="maximum-size">444</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">24</property>
<property name="margin-bottom">24</property>
<property name="margin-start">24</property>
<property name="margin-end">24</property>
<child type="top">
<object class="GtkLabel" id="heading">
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="max-width-chars">20</property>
<property name="justify">center</property>
<property name="xalign">0.5</property>
<property name="label" translatable="yes">New Room</property>
<style>
<class name="title-2"/>
</style>
</object>
</child>
<child>
<object class="GtkBox" id="content">
<property name="orientation">vertical</property>
<property name="spacing">18</property>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Name</property>
<child>
<object class="AdwEntryRow" id="room_name">
<property name="title" translatable="yes">Name</property>
<signal name="changed" handler="validate_form" swapped="yes"/>
<signal name="entry-activated" handler="create_room" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup" id="topic_group">
<property name="title" translatable="yes">Description (Optional)</property>
<child>
<object class="GtkScrolledWindow">
<property name="height-request">120</property>
<child>
<object class="GtkTextView" id="topic_text_view">
<property name="hexpand">True</property>
<property name="wrap-mode">word-char</property>
<style>
<class name="inline"/>
</style>
<accessibility>
<relation name="labelled-by">topic_group</relation>
</accessibility>
</object>
</child>
<style>
<class name="card"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title" translatable="yes">Room Visibility</property>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Private</property>
<property name="subtitle" translatable="yes">Only invited people can join this room</property>
<property name="activatable_widget">visibility_private</property>
<child type="prefix">
<object class="GtkCheckButton" id="visibility_private">
<property name="valign">center</property>
<property name="active">True</property>
<signal name="toggled" handler="validate_form" swapped="yes"/>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Public</property>
<property name="subtitle" translatable="yes">Anyone can find and join this room</property>
<property name="activatable_widget">visibility_public</property>
<child type="prefix">
<object class="GtkCheckButton" id="visibility_public">
<property name="valign">center</property>
<property name="group">visibility_private</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="visible" bind-source="visibility_private" bind-property="active" bind-flags="sync-create"/>
<property name="margin-top">12</property>
<child>
<object class="AdwSwitchRow" id="encryption">
<property name="title" translatable="yes">End-to-End Encryption</property>
<property name="subtitle" translatable="yes">Cannot be disabled later</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="visible" bind-source="visibility_public" bind-property="active" bind-flags="sync-create"/>
<property name="margin-top">12</property>
<child>
<object class="SubstringEntryRow" id="room_address">
<property name="title" translatable="yes">Main Address</property>
<!-- Translators: This is the placeholder for the first part of a room address, as in '@my-room:example.org'. -->
<property name="placeholder-text" translatable="yes">my-room</property>
<property name="accessible-description" translatable="yes">First part of the address, for example “my-room”</property>
<property name="prefix-text">#</property>
<property name="hide-add-button">True</property>
<signal name="changed" handler="validate_form" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkRevealer" id="room_address_error_revealer">
<property name="child">
<object class="GtkLabel" id="room_address_error">
<style>
<class name="error"/>
<class name="caption"/>
</style>
<property name="wrap">True</property>
<property name="wrap-mode">word-char</property>
<property name="xalign">0.0</property>
<property name="margin-top">6</property>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="LoadingButton" id="create_button">
<property name="content-label" translatable="yes">_Create Room</property>
<property name="use_underline">True</property>
<property name="sensitive">False</property>
<property name="halign">center</property>
<property name="valign">end</property>
<property name="vexpand">true</property>
<signal name="clicked" handler="create_room" swapped="yes"/>
<style>
<class name="suggested-action"/>
<class name="standalone-button"/>
<class name="pill"/>
</style>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</property>
</template>
</interface>

202
src/session/view/event_details_dialog.blp

@ -0,0 +1,202 @@
using Gtk 4.0;
using Adw 1;
using GtkSource 5;
template $EventDetailsDialog: $ToastableDialog {
title: _("Event Details");
content-width: 500;
content-height: 400;
styles [
"event-details-dialog",
]
child-content: Adw.NavigationView navigation_view {
Adw.NavigationPage {
title: _("Event Details");
tag: "details";
child: Adw.ToolbarView {
[top]
Adw.HeaderBar {}
content: Gtk.ScrolledWindow {
hscrollbar-policy: never;
vexpand: true;
child: Adw.Clamp {
vexpand: true;
margin-top: 12;
margin-bottom: 12;
margin-start: 12;
margin-end: 12;
child: Gtk.Box {
orientation: vertical;
spacing: 24;
Gtk.Box {
orientation: vertical;
spacing: 12;
Label {
visible: bind template.event as <$RoomEvent>.is-edited;
label: _("Original Event");
ellipsize: end;
xalign: 0;
styles [
"heading",
]
}
Gtk.ListBox {
styles [
"boxed-list",
]
$CopyableRow original_event_id_row {
main-title: "subtitle";
title: _("Event ID");
subtitle: bind template.event as <$RoomEvent>.event-id-string;
copy-button-tooltip-text: _("Copy Event ID");
toast-text: _("Event ID copied to clipboard");
}
$CopyableRow room_id_row {
main-title: "subtitle";
title: _("Room ID");
subtitle: bind template.event as <$RoomEvent>.timeline as <$Timeline>.room as <$Room>.room-id-string;
copy-button-tooltip-text: _("Copy Room ID");
toast-text: _("Room ID copied to clipboard");
}
Adw.ActionRow sender_id_row {
title: _("Sender ID");
subtitle: bind template.event as <$RoomEvent>.sender-id-string;
styles [
"property",
]
Gtk.Button {
valign: center;
icon-name: "user-info-symbolic";
tooltip-text: _("Show Profile");
clicked => $open_sender_profile() swapped;
styles [
"flat",
]
}
}
$CopyableRow original_timestamp_row {
main-title: "subtitle";
title: _("Timestamp");
subtitle: bind template.event as <$RoomEvent>.formatted-timestamp;
copy-button-tooltip-text: _("Copy Timestamp");
toast-text: _("Timestamp copied to clipboard");
}
Adw.ButtonRow {
visible: bind template.event as <$RoomEvent>.has-source;
selectable: false;
title: _("View Source");
end-icon-name: "go-next-symbolic";
activated => $show_original_source() swapped;
}
}
}
Gtk.Box {
visible: bind $string_not_empty(template.event as <$RoomEvent>.latest-edit-source) as <bool>;
orientation: vertical;
spacing: 12;
Gtk.Label {
label: _("Latest Edit");
ellipsize: end;
xalign: 0;
styles [
"heading",
]
}
Gtk.ListBox {
styles [
"boxed-list",
]
$CopyableRow edit_event_id_row {
main-title: "subtitle";
title: _("Event ID");
subtitle: bind template.event as <$RoomEvent>.latest-edit-event-id-string;
copy-button-tooltip-text: _("Copy Event ID");
toast-text: _("Event ID copied to clipboard");
}
$CopyableRow edit_timestamp_row {
main-title: "subtitle";
title: _("Timestamp");
subtitle: bind template.event as <$RoomEvent>.latest-edit-formatted-timestamp;
copy-button-tooltip-text: _("Copy Timestamp");
toast-text: _("Timestamp copied to clipboard");
}
Adw.ButtonRow {
selectable: false;
title: _("View Source");
end-icon-name: "go-next-symbolic";
activated => $show_edit_source() swapped;
}
}
}
};
};
};
};
}
Adw.NavigationPage source_page {
tag: "source";
child: Adw.ToolbarView {
[top]
Adw.HeaderBar {
[end]
Gtk.Button {
icon-name: "copy-symbolic";
focus-on-click: false;
tooltip-text: _("Copy Source");
valign: center;
clicked => $copy_source() swapped;
styles [
"flat",
]
}
}
content: Gtk.ScrolledWindow {
child: GtkSource.View source_view {
can-focus: false;
editable: false;
hexpand: true;
vexpand: true;
wrap-mode: word_char;
left-margin: 12;
right-margin: 12;
top-margin: 12;
bottom-margin: 12;
styles [
"monospace",
]
};
};
};
}
};
}

266
src/session/view/event_details_dialog.ui

@ -1,266 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="EventDetailsDialog" parent="ToastableDialog">
<property name="title" translatable="yes">Event Details</property>
<property name="content-width">500</property>
<property name="content-height">400</property>
<style>
<class name="event-details-dialog"/>
</style>
<property name="child-content">
<object class="AdwNavigationView" id="navigation_view">
<child>
<object class="AdwNavigationPage">
<property name="title" translatable="yes">Event Details</property>
<property name="tag">details</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar"/>
</child>
<property name="content">
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="vexpand">true</property>
<property name="child">
<object class="AdwClamp">
<property name="vexpand">True</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="child">
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">24</property>
<child>
<object class="GtkBox">
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel">
<binding name="visible">
<lookup name="is-edited">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<property name="label" translatable="yes">Original Event</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<style>
<class name="heading"/>
</style>
</object>
</child>
<child>
<object class="GtkListBox">
<style>
<class name="boxed-list"/>
</style>
<child>
<object class="CopyableRow" id="original_event_id_row">
<property name="main-title">subtitle</property>
<property name="title" translatable="yes">Event ID</property>
<binding name="subtitle">
<lookup name="event-id-string">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<property name="copy-button-tooltip-text" translatable="yes">Copy Event ID</property>
<property name="toast-text" translatable="yes">Event ID copied to clipboard</property>
</object>
</child>
<child>
<object class="CopyableRow" id="room_id_row">
<property name="main-title">subtitle</property>
<property name="title" translatable="yes">Room ID</property>
<binding name="subtitle">
<lookup name="room-id-string">
<lookup name="room">
<lookup name="timeline">
<lookup name="event">EventDetailsDialog</lookup>q
</lookup>
</lookup>
</lookup>
</binding>
<property name="copy-button-tooltip-text" translatable="yes">Copy Room ID</property>
<property name="toast-text" translatable="yes">Room ID copied to clipboard</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="sender_id_row">
<property name="title" translatable="yes">Sender ID</property>
<binding name="subtitle">
<lookup name="sender-id-string">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<style>
<class name="property"/>
</style>
<child>
<object class="GtkButton">
<property name="valign">center</property>
<property name="icon-name">user-info-symbolic</property>
<property name="tooltip-text" translatable="yes">Show Profile</property>
<signal name="clicked" handler="open_sender_profile" swapped="yes"/>
<style>
<class name="flat"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="CopyableRow" id="original_timestamp_row">
<property name="main-title">subtitle</property>
<property name="title" translatable="yes">Timestamp</property>
<binding name="subtitle">
<lookup name="formatted-timestamp">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<property name="copy-button-tooltip-text" translatable="yes">Copy Timestamp</property>
<property name="toast-text" translatable="yes">Timestamp copied to clipboard</property>
</object>
</child>
<child>
<object class="AdwButtonRow">
<binding name="visible">
<lookup name="has-source">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<property name="selectable">False</property>
<property name="title" translatable="yes">View Source</property>
<property name="end-icon-name">go-next-symbolic</property>
<signal name="activated" handler="show_original_source" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="GtkBox">
<binding name="visible">
<closure type="gboolean" function="string_not_empty">
<lookup name="latest-edit-source">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</closure>
</binding>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel">
<property name="label" translatable="yes">Latest Edit</property>
<property name="ellipsize">end</property>
<property name="xalign">0</property>
<style>
<class name="heading"/>
</style>
</object>
</child>
<child>
<object class="GtkListBox">
<style>
<class name="boxed-list"/>
</style>
<child>
<object class="CopyableRow" id="edit_event_id_row">
<property name="main-title">subtitle</property>
<property name="title" translatable="yes">Event ID</property>
<binding name="subtitle">
<lookup name="latest-edit-event-id-string">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<property name="copy-button-tooltip-text" translatable="yes">Copy Event ID</property>
<property name="toast-text" translatable="yes">Event ID copied to clipboard</property>
</object>
</child>
<child>
<object class="CopyableRow" id="edit_timestamp_row">
<property name="main-title">subtitle</property>
<property name="title" translatable="yes">Timestamp</property>
<binding name="subtitle">
<lookup name="latest-edit-formatted-timestamp">
<lookup name="event">EventDetailsDialog</lookup>
</lookup>
</binding>
<property name="copy-button-tooltip-text" translatable="yes">Copy Timestamp</property>
<property name="toast-text" translatable="yes">Timestamp copied to clipboard</property>
</object>
</child>
<child>
<object class="AdwButtonRow">
<property name="selectable">False</property>
<property name="title" translatable="yes">View Source</property>
<property name="end-icon-name">go-next-symbolic</property>
<signal name="activated" handler="show_edit_source" swapped="yes"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="AdwNavigationPage" id="source_page">
<property name="tag">source</property>
<property name="child">
<object class="AdwToolbarView">
<child type="top">
<object class="AdwHeaderBar">
<child type="end">
<object class="GtkButton">
<property name="icon-name">copy-symbolic</property>
<property name="focus-on-click">False</property>
<property name="tooltip-text" translatable="yes">Copy Source</property>
<property name="valign">center</property>
<signal name="clicked" handler="copy_source" swapped="yes" />
<style>
<class name="flat"/>
</style>
</object>
</child>
</object>
</child>
<property name="content">
<object class="GtkScrolledWindow">
<property name="child">
<object class="GtkSourceView" id="source_view">
<property name="can_focus">False</property>
<property name="editable">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="wrap-mode">word-char</property>
<property name="left-margin">12</property>
<property name="right-margin">12</property>
<property name="top-margin">12</property>
<property name="bottom-margin">12</property>
<style>
<class name="monospace"/>
</style>
</object>
</property>
</object>
</property>
</object>
</property>
</object>
</child>
</object>
</property>
</template>
</interface>

141
src/session/view/media_viewer.blp

@ -0,0 +1,141 @@
using Gtk 4.0;
using Adw 1;
menu media-secondary-menu-model {
section {
item {
label: _("_Copy Image");
action: "media-viewer.copy-image";
hidden-when: "action-disabled";
}
item {
label: _("S_ave Image");
action: "media-viewer.save-image";
hidden-when: "action-disabled";
}
item {
label: _("S_ave Video");
action: "media-viewer.save-video";
hidden-when: "action-disabled";
}
item {
label: _("Copy Message _Link");
action: "media-viewer.permalink";
hidden-when: "action-disabled";
}
}
}
menu media-context-menu-model {
section {
item {
label: _("_Copy Image");
action: "media-viewer.copy-image";
hidden-when: "action-disabled";
}
item {
label: _("S_ave Image");
action: "media-viewer.save-image";
hidden-when: "action-disabled";
}
item {
label: _("S_ave Video");
action: "media-viewer.save-video";
hidden-when: "action-disabled";
}
}
}
template $MediaViewer: Gtk.Widget {
Adw.ToolbarView toolbar_view {
extend-content-to-top-edge: bind template.fullscreened;
reveal-top-bars: bind template.fullscreened inverted;
overflow: visible;
[top]
Gtk.HeaderBar header_bar {
title-widget: Gtk.Label {
label: bind template.filename;
single-line-mode: true;
ellipsize: end;
styles [
"title",
]
};
[start]
Gtk.Button back {
icon-name: "go-previous-symbolic";
action-name: "media-viewer.close";
tooltip-text: _("Back");
accessibility {
label: _("Back");
}
}
[end]
Gtk.MenuButton menu {
icon-name: "menu-secondary-symbolic";
menu-model: media-secondary-menu-model;
tooltip-text: _("Media Menu");
primary: true;
accessibility {
label: _("Media Menu");
}
}
[end]
Gtk.Button {
visible: bind template.fullscreened inverted;
icon-name: "fullscreen-symbolic";
action-name: "win.toggle-fullscreen";
tooltip-text: _("Fullscreen");
accessibility {
label: _("Fullscreen");
}
}
[end]
Gtk.Button {
visible: bind template.fullscreened;
icon-name: "restore-symbolic";
action-name: "win.toggle-fullscreen";
tooltip-text: _("Exit Fullscreen");
accessibility {
label: _("Exit Fullscreen");
}
}
}
content: $ScaleRevealer revealer {
child: $MediaContentViewer media {
autoplay: true;
has-context-menu: true;
popover: Gtk.PopoverMenu {
has-arrow: false;
halign: start;
menu-model: media-context-menu-model;
};
};
};
}
Gtk.EventControllerMotion {
motion => $handle_motion() swapped;
}
Gtk.GestureClick {
released => $handle_click() swapped;
}
}

141
src/session/view/media_viewer.ui

@ -1,141 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<menu id="media-secondary-menu-model">
<section>
<item>
<attribute name="label" translatable="yes">_Copy Image</attribute>
<attribute name="action">media-viewer.copy-image</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">S_ave Image</attribute>
<attribute name="action">media-viewer.save-image</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">S_ave Video</attribute>
<attribute name="action">media-viewer.save-video</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Copy Message _Link</attribute>
<attribute name="action">media-viewer.permalink</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
</section>
</menu>
<menu id="media-context-menu-model">
<section>
<item>
<attribute name="label" translatable="yes">_Copy Image</attribute>
<attribute name="action">media-viewer.copy-image</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">S_ave Image</attribute>
<attribute name="action">media-viewer.save-image</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
<item>
<attribute name="label" translatable="yes">S_ave Video</attribute>
<attribute name="action">media-viewer.save-video</attribute>
<attribute name="hidden-when">action-disabled</attribute>
</item>
</section>
</menu>
<template class="MediaViewer" parent="GtkWidget">
<child>
<object class="AdwToolbarView" id="toolbar_view">
<property name="extend-content-to-top-edge" bind-source="MediaViewer" bind-property="fullscreened" bind-flags="sync-create"/>
<property name="reveal-top-bars" bind-source="MediaViewer" bind-property="fullscreened" bind-flags="sync-create|invert-boolean"/>
<property name="overflow">visible</property>
<child type="top">
<object class="GtkHeaderBar" id="header_bar">
<property name="title-widget">
<object class="GtkLabel">
<binding name="label">
<lookup name="filename">MediaViewer</lookup>
</binding>
<property name="single-line-mode">True</property>
<property name="ellipsize">end</property>
<style>
<class name="title"/>
</style>
</object>
</property>
<child type="start">
<object class="GtkButton" id="back">
<property name="icon-name">go-previous-symbolic</property>
<property name="action-name">media-viewer.close</property>
<property name="tooltip-text" translatable="yes">Back</property>
<accessibility>
<property name="label" translatable="yes">Back</property>
</accessibility>
</object>
</child>
<child type="end">
<object class="GtkMenuButton" id="menu">
<property name="icon-name">menu-secondary-symbolic</property>
<property name="menu-model">media-secondary-menu-model</property>
<property name="tooltip-text" translatable="yes">Media Menu</property>
<accessibility>
<property name="label" translatable="yes">Media Menu</property>
</accessibility>
<property name="primary">True</property>
</object>
</child>
<child type="end">
<object class="GtkButton">
<property name="visible" bind-source="MediaViewer" bind-property="fullscreened" bind-flags="sync-create|invert-boolean"/>
<property name="icon-name">fullscreen-symbolic</property>
<property name="action-name">win.toggle-fullscreen</property>
<property name="tooltip-text" translatable="yes">Fullscreen</property>
<accessibility>
<property name="label" translatable="yes">Fullscreen</property>
</accessibility>
</object>
</child>
<child type="end">
<object class="GtkButton">
<property name="visible" bind-source="MediaViewer" bind-property="fullscreened" bind-flags="sync-create"/>
<property name="icon-name">restore-symbolic</property>
<property name="action-name">win.toggle-fullscreen</property>
<property name="tooltip-text" translatable="yes">Exit Fullscreen</property>
<accessibility>
<property name="label" translatable="yes">Exit Fullscreen</property>
</accessibility>
</object>
</child>
</object>
</child>
<property name="content">
<object class="ScaleRevealer" id="revealer">
<property name="child">
<object class="MediaContentViewer" id="media">
<property name="autoplay">true</property>
<property name="popover">
<object class="GtkPopoverMenu">
<property name="has-arrow">False</property>
<property name="halign">start</property>
<property name="menu-model">media-context-menu-model</property>
</object>
</property>
<property name="has-context-menu">True</property>
</object>
</property>
</object>
</property>
</object>
</child>
<child>
<object class="GtkEventControllerMotion">
<signal name="motion" handler="handle_motion" swapped="yes"/>
</object>
</child>
<child>
<object class="GtkGestureClick">
<signal name="released" handler="handle_click" swapped="yes"/>
</object>
</child>
</template>
</interface>

54
src/session/view/session_view.blp

@ -0,0 +1,54 @@
using Gtk 4.0;
using Adw 1;
template $SessionView: Adw.Bin {
child: Gtk.Stack stack {
visible-child: overlay;
transition-type: crossfade;
Gtk.Overlay overlay {
[overlay]
$MediaViewer media_viewer {
visible: false;
}
Adw.NavigationSplitView split_view {
collapsed: bind template.root as <$Window>.compact;
sidebar: $Sidebar sidebar {
user: bind template.session as <$Session>.user;
list-model: bind template.session as <$Session>.sidebar-list-model;
};
content: $Content content {
session: bind template.session;
only-view: bind template.root as <$Window>.compact;
};
}
}
};
Gtk.ShortcutController {
propagation-phase: capture;
Gtk.Shortcut {
trigger: "<Control>Page_Up";
action: "action(session.select-prev-room)";
}
Gtk.Shortcut {
trigger: "<Control>Page_Down";
action: "action(session.select-next-room)";
}
Gtk.Shortcut {
trigger: "<Control><Shift>Page_Down";
action: "action(session.select-next-unread-room)";
}
Gtk.Shortcut {
trigger: "<Control><Shift>Page_Up";
action: "action(session.select-prev-unread-room)";
}
}
}

82
src/session/view/session_view.ui

@ -1,82 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="SessionView" parent="AdwBin">
<property name="child">
<object class="GtkStack" id="stack">
<property name="visible-child">overlay</property>
<property name="transition-type">crossfade</property>
<child>
<object class="GtkOverlay" id="overlay">
<child type="overlay">
<object class="MediaViewer" id="media_viewer">
<property name="visible">False</property>
</object>
</child>
<child>
<object class="AdwNavigationSplitView" id="split_view">
<binding name="collapsed">
<lookup type="Window" name="compact">
<lookup name="root">SessionView</lookup>
</lookup>
</binding>
<property name="sidebar">
<object class="Sidebar" id="sidebar">
<binding name="user">
<lookup name="user">
<lookup name="session">SessionView</lookup>
</lookup>
</binding>
<binding name="list-model">
<lookup name="sidebar-list-model">
<lookup name="session">SessionView</lookup>
</lookup>
</binding>
</object>
</property>
<property name="content">
<object class="Content" id="content">
<property name="session" bind-source="SessionView" bind-property="session" bind-flags="sync-create"/>
<binding name="only-view">
<lookup type="Window" name="compact">
<lookup name="root">SessionView</lookup>
</lookup>
</binding>
</object>
</property>
</object>
</child>
</object>
</child>
</object>
</property>
<child>
<object class='GtkShortcutController'>
<property name='propagation-phase'>capture</property>
<child>
<object class='GtkShortcut'>
<property name='trigger'>&lt;Control&gt;Page_Up</property>
<property name='action'>action(session.select-prev-room)</property>
</object>
</child>
<child>
<object class='GtkShortcut'>
<property name='trigger'>&lt;Control&gt;Page_Down</property>
<property name='action'>action(session.select-next-room)</property>
</object>
</child>
<child>
<object class='GtkShortcut'>
<property name='trigger'>&lt;Control&gt;&lt;Shift&gt;Page_Down</property>
<property name='action'>action(session.select-next-unread-room)</property>
</object>
</child>
<child>
<object class='GtkShortcut'>
<property name='trigger'>&lt;Control&gt;&lt;Shift&gt;Page_Up</property>
<property name='action'>action(session.select-prev-unread-room)</property>
</object>
</child>
</object>
</child>
</template>
</interface>

5
src/ui-resources.gresource.xml

@ -2,11 +2,6 @@
<gresources>
<gresource prefix="/org/gnome/Fractal/ui/">
<file compressed="true" preprocess="xml-stripblanks">session/view/account_settings/general_page/change_password_subpage.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/create_direct_chat_dialog/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/create_room_dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/event_details_dialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/media_viewer.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/session_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/sidebar/icon_item_row.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/sidebar/mod.ui</file>
<file compressed="true" preprocess="xml-stripblanks">session/view/sidebar/room_row.ui</file>

Loading…
Cancel
Save