diff --git a/.idea/go.imports.xml b/.idea/go.imports.xml new file mode 100644 index 0000000..644cdf0 --- /dev/null +++ b/.idea/go.imports.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 16d9e98..dfb10d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,29 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "Tofu" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "dirs", - "dotenvy", - "eyeball-im", - "futures-util", - "hex", - "matrix-sdk", - "matrix-sdk-ui", - "rand 0.10.3", - "serde", - "serde_json", - "slint", - "slint-build", - "tokio", - "tracing", - "tracing-subscriber", -] - [[package]] name = "ab_glyph" version = "0.2.32" @@ -6879,6 +6856,29 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" +[[package]] +name = "tofu" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "dirs", + "dotenvy", + "eyeball-im", + "futures-util", + "hex", + "matrix-sdk", + "matrix-sdk-ui", + "rand 0.10.3", + "serde", + "serde_json", + "slint", + "slint-build", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "tokio" version = "1.53.2" diff --git a/Cargo.toml b/Cargo.toml index c8f2f25..d95632b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] -name = "Tofu" -authors = ["Inako"] +name = "tofu" +authors = ["Inako", "Thoq"] version = "0.1.0" edition = "2024" diff --git a/assets/translations/en_US/LC_MESSAGES/tofu.po b/assets/translations/en_US/LC_MESSAGES/tofu.po new file mode 100644 index 0000000..0698e21 --- /dev/null +++ b/assets/translations/en_US/LC_MESSAGES/tofu.po @@ -0,0 +1,151 @@ +# +msgid "" +msgstr "" +"Project-Id-Version: Tofu 0.1.0\n" +"POT-Creation-Date: 2026-10-09 21:50+0000\n" +"PO-Revision-Date: 2026-10-09 20:26+0000\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. Reply preview: {0} is the sender, {1} the start of the replied-to text. +#: ui/components/message-list.slint:112 +msgctxt "ChatMessage" +msgid "↳ {0} {1}" +msgstr "↳ {0} {1}" + +#: ui/components/message-list.slint:179 +msgctxt "ChatMessage" +msgid "(edited)" +msgstr "(edited)" + +#. {} is the channel name; the leading "#" is the channel prefix. +#: ui/components/message-list.slint:274 +msgctxt "RoomStart" +msgid "Welcome to #{}!" +msgstr "Welcome to #{}!" + +#. {} is the channel name; the leading "#" is the channel prefix. +#: ui/components/message-list.slint:283 +msgctxt "RoomStart" +msgid "This is the start of the #{} channel." +msgstr "This is the start of the #{} channel." + +#: ui/components/message-list.slint:395 +msgctxt "MessageList" +msgid "Loading history…" +msgstr "Loading history…" + +#: ui/login.slint:35 +msgctxt "LoginScreen" +msgid "Welcome back!" +msgstr "Welcome back!" + +#: ui/login.slint:43 +msgctxt "LoginScreen" +msgid "Sign in with your Matrix account" +msgstr "Sign in with your Matrix account" + +#: ui/login.slint:54 +msgctxt "LoginScreen" +msgid "HOMESERVER" +msgstr "HOMESERVER" + +#: ui/login.slint:61 +msgctxt "LoginScreen" +msgid "matrix.org" +msgstr "matrix.org" + +#: ui/login.slint:70 +msgctxt "LoginScreen" +msgid "USERNAME" +msgstr "USERNAME" + +#: ui/login.slint:77 +msgctxt "LoginScreen" +msgid "user (or @user:matrix.org)" +msgstr "user (or @user:matrix.org)" + +#: ui/login.slint:86 +msgctxt "LoginScreen" +msgid "PASSWORD" +msgstr "PASSWORD" + +#: ui/login.slint:115 +msgctxt "LoginScreen" +msgid "Signing in…" +msgstr "Signing in…" + +#: ui/login.slint:115 +msgctxt "LoginScreen" +msgid "Log In" +msgstr "Log In" + +#: ui/main.slint:18 ui/main.slint:84 +msgctxt "MainWindow" +msgid "Tofu" +msgstr "Tofu" + +#. Heading of the sidebar in the Home view, which lists direct messages. +#: ui/main.slint:45 ui/main.slint:124 +msgctxt "MainWindow" +msgid "Direct Messages" +msgstr "Direct Messages" + +#: ui/main.slint:118 +msgctxt "MainWindow" +msgid "Create / join a space" +msgstr "Create / join a space" + +#: ui/main.slint:133 +msgctxt "MainWindow" +msgid "Space settings" +msgstr "Space settings" + +#: ui/main.slint:159 +msgctxt "MainWindow" +msgid "Threads" +msgstr "Threads" + +#: ui/main.slint:162 +msgctxt "MainWindow" +msgid "Pinned messages" +msgstr "Pinned messages" + +#: ui/main.slint:165 +msgctxt "MainWindow" +msgid "Search" +msgstr "Search" + +#. {} is the room name (or the person's name in a DM). Keep the "#" / "@". +#: ui/main.slint:207 +msgctxt "MainWindow" +msgid "Message @{}" +msgstr "Message @{}" + +#. {} is the room name (or the person's name in a DM). Keep the "#" / "@". +#: ui/main.slint:207 +msgctxt "MainWindow" +msgid "Message #{}" +msgstr "Message #{}" + +#. Banner above the message box. {} is the name of the person replied to. +#: ui/main.slint:209 +msgctxt "MainWindow" +msgid "Editing message" +msgstr "Editing message" + +#. Banner above the message box. {} is the name of the person replied to. +#: ui/main.slint:209 +msgctxt "MainWindow" +msgid "Replying to {}" +msgstr "Replying to {}" + +#: ui/main.slint:231 +msgctxt "MainWindow" +msgid "File uploads" +msgstr "File uploads" diff --git a/assets/translations/tofu.pot b/assets/translations/tofu.pot new file mode 100644 index 0000000..64e0dcf --- /dev/null +++ b/assets/translations/tofu.pot @@ -0,0 +1,151 @@ +# +msgid "" +msgstr "" +"Project-Id-Version: tofu 0.1.0\n" +"POT-Creation-Date: 2026-10-09 21:50+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#. Reply preview: {0} is the sender, {1} the start of the replied-to text. +#: ui/components/message-list.slint:112 +msgctxt "ChatMessage" +msgid "↳ {0} {1}" +msgstr "" + +#: ui/components/message-list.slint:179 +msgctxt "ChatMessage" +msgid "(edited)" +msgstr "" + +#. {} is the channel name; the leading "#" is the channel prefix. +#: ui/components/message-list.slint:274 +msgctxt "RoomStart" +msgid "Welcome to #{}!" +msgstr "" + +#. {} is the channel name; the leading "#" is the channel prefix. +#: ui/components/message-list.slint:283 +msgctxt "RoomStart" +msgid "This is the start of the #{} channel." +msgstr "" + +#: ui/components/message-list.slint:395 +msgctxt "MessageList" +msgid "Loading history…" +msgstr "" + +#: ui/login.slint:35 +msgctxt "LoginScreen" +msgid "Welcome back!" +msgstr "" + +#: ui/login.slint:43 +msgctxt "LoginScreen" +msgid "Sign in with your Matrix account" +msgstr "" + +#: ui/login.slint:54 +msgctxt "LoginScreen" +msgid "HOMESERVER" +msgstr "" + +#: ui/login.slint:61 +msgctxt "LoginScreen" +msgid "matrix.org" +msgstr "" + +#: ui/login.slint:70 +msgctxt "LoginScreen" +msgid "USERNAME" +msgstr "" + +#: ui/login.slint:77 +msgctxt "LoginScreen" +msgid "user (or @user:matrix.org)" +msgstr "" + +#: ui/login.slint:86 +msgctxt "LoginScreen" +msgid "PASSWORD" +msgstr "" + +#: ui/login.slint:115 +msgctxt "LoginScreen" +msgid "Signing in…" +msgstr "" + +#: ui/login.slint:115 +msgctxt "LoginScreen" +msgid "Log In" +msgstr "" + +#: ui/main.slint:18 ui/main.slint:84 +msgctxt "MainWindow" +msgid "Tofu" +msgstr "" + +#. Heading of the sidebar in the Home view, which lists direct messages. +#: ui/main.slint:45 ui/main.slint:124 +msgctxt "MainWindow" +msgid "Direct Messages" +msgstr "" + +#: ui/main.slint:118 +msgctxt "MainWindow" +msgid "Create / join a space" +msgstr "" + +#: ui/main.slint:133 +msgctxt "MainWindow" +msgid "Space settings" +msgstr "" + +#: ui/main.slint:159 +msgctxt "MainWindow" +msgid "Threads" +msgstr "" + +#: ui/main.slint:162 +msgctxt "MainWindow" +msgid "Pinned messages" +msgstr "" + +#: ui/main.slint:165 +msgctxt "MainWindow" +msgid "Search" +msgstr "" + +#. {} is the room name (or the person's name in a DM). Keep the "#" / "@". +#: ui/main.slint:207 +msgctxt "MainWindow" +msgid "Message @{}" +msgstr "" + +#. {} is the room name (or the person's name in a DM). Keep the "#" / "@". +#: ui/main.slint:207 +msgctxt "MainWindow" +msgid "Message #{}" +msgstr "" + +#. Banner above the message box. {} is the name of the person replied to. +#: ui/main.slint:209 +msgctxt "MainWindow" +msgid "Editing message" +msgstr "" + +#. Banner above the message box. {} is the name of the person replied to. +#: ui/main.slint:209 +msgctxt "MainWindow" +msgid "Replying to {}" +msgstr "" + +#: ui/main.slint:231 +msgctxt "MainWindow" +msgid "File uploads" +msgstr "" diff --git a/build.rs b/build.rs index ef2013c..8032b86 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,8 @@ use slint_build::CompilerConfiguration; fn main() { - let config = CompilerConfiguration::new().with_style("fluent-dark".to_owned()); + let config = CompilerConfiguration::new() + .with_style("fluent-dark".to_owned()) + .with_bundled_translations("assets/translations"); slint_build::compile_with_config("ui/main.slint", config).expect("Failed to compile Slint UI"); -} \ No newline at end of file +} diff --git a/scripts/update-translations.sh b/scripts/update-translations.sh new file mode 100755 index 0000000..332af8d --- /dev/null +++ b/scripts/update-translations.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +set -euo pipefail + +cd "$(dirname "${BASH_SOURCE[0]}")/.." + +# janky aaah +domain=$(sed -n '/^\[package\]/,/^\[/s/^name = "\(.*\)"$/\1/p' Cargo.toml) +version=$(sed -n '/^\[package\]/,/^\[/s/^version = "\(.*\)"$/\1/p' Cargo.toml) +[ -n "$domain" ] || { echo "error: could not find the package name in Cargo.toml" >&2; exit 1; } +dir=assets/translations +pot=$dir/$domain.pot + +for tool in slint-tr-extractor msginit msgmerge msgcat; do + command -v "$tool" > /dev/null || { echo "error: '$tool' is not installed, see docs/TRANSLATIONS.md" >&2; exit 1; } +done + +fresh=$(mktemp) +trap 'rm -f "$fresh"' EXIT + +find ui -name '*.slint' | LC_ALL=C sort | xargs slint-tr-extractor --package-name "$domain" --package-version "$version" -o "$fresh" + +msgcat --no-wrap "$fresh" > /dev/null || { echo "error: the extracted template is not valid, is a translator comment longer than 76 characters?" >&2; exit 1; } + +if [ -f "$pot" ] && diff -q <(grep -v '^"POT-Creation-Date:' "$pot") <(grep -v '^"POT-Creation-Date:' "$fresh") > /dev/null; then + echo "$pot is up to date" +else + cp "$fresh" "$pot" + echo "updated $pot" +fi + +for lang in "$@"; do + po=$dir/$lang/LC_MESSAGES/$domain.po + if [ -e "$po" ]; then + echo "$po already exists" >&2 + else + mkdir -p "$(dirname "$po")" + msginit --no-translator --locale="$lang" --input="$pot" --output-file="$po" + fi +done + +for po in "$dir"/*/LC_MESSAGES/"$domain".po; do + [ -e "$po" ] || continue + msgmerge --update --backup=none --quiet "$po" "$pot" + echo "merged into $po" +done diff --git a/src/bridge.rs b/src/bridge.rs index fb81c68..fb428b3 100644 --- a/src/bridge.rs +++ b/src/bridge.rs @@ -30,10 +30,11 @@ pub fn toast(ui: &MainWindow, text: &str) { fn typing_text(names: &[String]) -> String { match names { + //todo: translate [] => String::new(), [a] => format!("{a} is typing..."), [a, b] => format!("{a} and {b} are typing..."), - _ => "Several people are typing...".to_owned() + _ => "Several people are typing...".to_owned(), } } @@ -46,7 +47,7 @@ pub fn apply(ui: &MainWindow, event: Event) { } Event::LoginBusy(busy) => { - ui.set_login_busy(busy) + ui.set_login_busy(busy); } Event::LoggedIn(user) => { @@ -58,16 +59,22 @@ pub fn apply(ui: &MainWindow, event: Event) { ui.set_screen(Screen::Main); } - Event::Servers { servers, home_unread, home_mentions } => { - let items: Vec = servers.into_iter() + Event::Servers { + servers, + home_unread, + home_mentions, + } => { + let items: Vec = servers + .into_iter() .map(|server| ServerItem { color: color_for(&server.id), initial: initial_of(&server.name).into(), id: server.id.into(), name: server.name.into(), unread: server.unread, - mentions: i32::try_from(server.mentions).unwrap_or(i32::MAX) - }).collect(); + mentions: i32::try_from(server.mentions).unwrap_or(i32::MAX), + }) + .collect(); ui.set_servers(model_of(items)); ui.set_home_unread(home_unread); @@ -77,13 +84,18 @@ pub fn apply(ui: &MainWindow, event: Event) { Event::Channels { title, channels } => { let selected: String = ui.get_selected_room_id().to_string(); - let first_openable: Option = channels.iter() - .find(|channel| channel.kind != model::ChannelKind::Category && channel.kind != model::ChannelKind::Voice) + let first_openable: Option = channels + .iter() + .find(|channel| { + channel.kind != model::ChannelKind::Category + && channel.kind != model::ChannelKind::Voice + }) .map(|channel| channel.id.clone()); let still_here: bool = channels.iter().any(|channel| channel.id == selected); - let items: Vec = channels.into_iter() + let items: Vec = channels + .into_iter() .map(|channel| ChannelItem { color: color_for(&channel.id), initial: initial_of(&channel.name).into(), @@ -98,27 +110,24 @@ pub fn apply(ui: &MainWindow, event: Event) { unread: channel.unread, mentions: i32::try_from(channel.mentions).unwrap_or(i32::MAX), joined: channel.joined, - is_dm: channel.is_dm - }).collect(); + is_dm: channel.is_dm, + }) + .collect(); ui.set_sidebar_title(title.into()); ui.set_channels(model_of(items)); if !still_here { - match first_openable { - Some(id) => { - ui.set_selected_room_id(id.clone().into()); - ui.invoke_select_room(id.into()); - } - - None => { - ui.set_selected_room_id(SharedString::new()); - ui.set_room_open(false); - ui.set_room_name(SharedString::new()); - ui.set_room_topic(SharedString::new()); - ui.set_messages(model_of(Vec::new())); - ui.set_members(model_of(Vec::new())); - } + if let Some(id) = first_openable { + ui.set_selected_room_id(id.clone().into()); + ui.invoke_select_room(id.into()); + } else { + ui.set_selected_room_id(SharedString::new()); + ui.set_room_open(false); + ui.set_room_name(SharedString::new()); + ui.set_room_topic(SharedString::new()); + ui.set_messages(model_of(Vec::new())); + ui.set_members(model_of(Vec::new())); } } } @@ -145,21 +154,23 @@ pub fn apply(ui: &MainWindow, event: Event) { } Event::Members(rows) => { - let items: Vec = rows.into_iter() + let items: Vec = rows + .into_iter() .map(|member| MemberItem { color: color_for(&member.id), initial: initial_of(&member.name).into(), id: member.id.into(), name: member.name.into(), is_header: member.is_header, - }).collect(); + }) + .collect(); ui.set_members(model_of(items)); } Event::Typing(names) => ui.set_typing_text(typing_text(&names).into()), Event::LoadingOlder(b) => ui.set_loading_older(b), - Event::Toast(text) => toast(ui, &text) + Event::Toast(text) => toast(ui, &text), } } @@ -191,12 +202,15 @@ fn message_item(message: model::MessageRow) -> MessageItem { reply_sender: reply_sender.into(), reply_body: reply_body.into(), reactions: model_of( - message.reactions.into_iter() + message + .reactions + .into_iter() .map(|reaction| ReactionItem { key: reaction.key.into(), count: i32::try_from(reaction.count).unwrap_or(i32::MAX), mine: reaction.mine, - }).collect(), + }) + .collect(), ), } } @@ -210,11 +224,14 @@ pub fn wire(ui: &MainWindow, tx: &UnboundedSender) { let tx = tx.clone(); move |homeserver, username, password| { - send(&tx, Command::Login { - homeserver: homeserver.into(), - username: username.into(), - password: password.into(), - }); + send( + &tx, + Command::Login { + homeserver: homeserver.into(), + username: username.into(), + password: password.into(), + }, + ); } }); @@ -225,7 +242,12 @@ pub fn wire(ui: &MainWindow, tx: &UnboundedSender) { ui.on_select_server({ let tx = tx.clone(); - move |id| send(&tx, Command::SelectServer(Some(id.to_string()).filter(|s| !s.is_empty()))) + move |id| { + send( + &tx, + Command::SelectServer(Some(id.to_string()).filter(|s| !s.is_empty())), + ); + } }); ui.on_select_room({ @@ -236,16 +258,27 @@ pub fn wire(ui: &MainWindow, tx: &UnboundedSender) { ui.on_send_message({ let tx = tx.clone(); move |reply_to, text| { - send(&tx, Command::Send { - reply_to: Some(reply_to.to_string()).filter(|s| !s.is_empty()), - text: text.into(), - }); + send( + &tx, + Command::Send { + reply_to: Some(reply_to.to_string()).filter(|s| !s.is_empty()), + text: text.into(), + }, + ); } }); ui.on_edit_message({ let tx = tx.clone(); - move |id, text| send(&tx, Command::Edit { id: id.into(), text: text.into() }) + move |id, text| { + send( + &tx, + Command::Edit { + id: id.into(), + text: text.into(), + }, + ); + } }); ui.on_delete_message({ @@ -255,7 +288,15 @@ pub fn wire(ui: &MainWindow, tx: &UnboundedSender) { ui.on_toggle_reaction({ let tx = tx.clone(); - move |id, key| send(&tx, Command::ToggleReaction { id: id.into(), key: key.into() }) + move |id, key| { + send( + &tx, + Command::ToggleReaction { + id: id.into(), + key: key.into(), + }, + ); + } }); ui.on_load_older({ diff --git a/src/helpers/mod.rs b/src/helpers/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/main.rs b/src/main.rs index e89c9c2..a7e5503 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ mod backend; mod model; pub mod bridge; +mod helpers; use std::sync::Arc; use slint::Weak; diff --git a/ui/components/message-list.slint b/ui/components/message-list.slint index 706cbf6..2bfbce0 100644 --- a/ui/components/message-list.slint +++ b/ui/components/message-list.slint @@ -108,7 +108,8 @@ component ChatMessage inherits Rectangle { x: Theme.gutter; width: parent.width - Theme.gutter - 16px; height: parent.height; - text: "↳ \{root.m.reply-sender} \{root.m.reply-body}"; + // Reply preview: {0} is the sender, {1} the start of the replied-to text. + text: @tr("↳ {0} {1}", root.m.reply-sender, root.m.reply-body); color: Theme.text-muted; font-size: 13px; vertical-alignment: center; @@ -175,7 +176,7 @@ component ChatMessage inherits Rectangle { } if root.m.edited: Text { - text: "(edited)"; + text: @tr("(edited)"); color: Theme.text-faint; font-size: 11px; } @@ -269,7 +270,8 @@ component RoomStart inherits Rectangle { padding-top: 32px; spacing: 6px; Text { - text: "Welcome to #\{root.room-name}!"; + // {} is the channel name; the leading "#" is the channel prefix. + text: @tr("Welcome to #{}!", root.room-name); color: Theme.text-header; font-size: 28px; font-weight: 700; @@ -277,7 +279,8 @@ component RoomStart inherits Rectangle { } Text { - text: "This is the start of the #\{root.room-name} channel."; + // {} is the channel name; the leading "#" is the channel prefix. + text: @tr("This is the start of the #{} channel.", root.room-name); color: Theme.text-muted; font-size: 15px; wrap: word-wrap; @@ -389,7 +392,7 @@ export component MessageList inherits Rectangle { border-radius: 12px; background: Theme.popup-bg; Text { - text: "Loading history…"; + text: @tr("Loading history…"); color: Theme.text-muted; font-size: 12px; horizontal-alignment: center; diff --git a/ui/login.slint b/ui/login.slint index 910d10f..3e0bdfb 100644 --- a/ui/login.slint +++ b/ui/login.slint @@ -32,7 +32,7 @@ export component LoginScreen inherits Rectangle { spacing: 6px; Text { - text: "Welcome back!"; + text: @tr("Welcome back!"); color: Theme.text-header; font-size: 24px; font-weight: 700; @@ -40,7 +40,7 @@ export component LoginScreen inherits Rectangle { } Text { - text: "Sign in with your Matrix account"; + text: @tr("Sign in with your Matrix account"); color: Theme.text-muted; font-size: 15px; horizontal-alignment: center; @@ -51,14 +51,14 @@ export component LoginScreen inherits Rectangle { } Text { - text: "HOMESERVER"; + text: @tr("HOMESERVER"); color: Theme.text-muted; font-size: 12px; font-weight: 700; } TextField { - placeholder: "matrix.org"; + placeholder: @tr("matrix.org"); text <=> root.homeserver; } @@ -67,14 +67,14 @@ export component LoginScreen inherits Rectangle { } Text { - text: "USERNAME"; + text: @tr("USERNAME"); color: Theme.text-muted; font-size: 12px; font-weight: 700; } TextField { - placeholder: "user (or @user:matrix.org)"; + placeholder: @tr("user (or @user:matrix.org)"); text <=> root.username; } @@ -83,7 +83,7 @@ export component LoginScreen inherits Rectangle { } Text { - text: "PASSWORD"; + text: @tr("PASSWORD"); color: Theme.text-muted; font-size: 12px; font-weight: 700; @@ -112,7 +112,7 @@ export component LoginScreen inherits Rectangle { border-radius: 4px; background: root.busy ? Theme.accent-hover : (login-touch.has-hover ? Theme.accent-hover : Theme.accent); Text { - text: root.busy ? "Signing in…" : "Log In"; + text: root.busy ? @tr("Signing in…") : @tr("Log In"); color: white; font-size: 16px; font-weight: 600; diff --git a/ui/main.slint b/ui/main.slint index 80793bb..ca14e83 100644 --- a/ui/main.slint +++ b/ui/main.slint @@ -15,7 +15,7 @@ import { MemberList } from "components/member-list.slint"; export { Screen, ServerItem, ChannelItem, MessageItem, MemberItem, ChannelKind, MessageKind, ReactionItem } from "types.slint"; export component MainWindow inherits Window { - title: "Tofu"; + title: @tr("Tofu"); min-width: 900px; min-height: 520px; preferred-width: 1280px; @@ -42,7 +42,7 @@ export component MainWindow inherits Window { in property <[MessageItem]> messages; in property <[MemberItem]> members; - in property sidebar-title: "Direct Messages"; + in property sidebar-title: @tr("Direct Messages"); in property room-name; in property room-topic; in property room-is-dm; @@ -81,7 +81,7 @@ export component MainWindow inherits Window { if root.screen == Screen.loading: Rectangle { background: Theme.rail-bg; Text { - text: "Tofu"; + text: @tr("Tofu"); color: Theme.text-muted; font-size: 32px; font-weight: 700; @@ -115,12 +115,13 @@ export component MainWindow inherits Window { root.select-server(id); } add-server => { - root.not-implemented("Create / join a space"); + root.not-implemented(@tr("Create / join a space")); } } ChannelList { - title: root.sidebar-title; + // Heading of the sidebar in the Home view, which lists direct messages. + title: root.selected-server-id == "" ? @tr("Direct Messages") : root.sidebar-title; is-home: root.selected-server-id == ""; channels: root.channels; selected-id: root.selected-room-id; @@ -129,7 +130,7 @@ export component MainWindow inherits Window { root.select-room(id); } open-server-menu => { - root.not-implemented("Space settings"); + root.not-implemented(@tr("Space settings")); } } } @@ -147,6 +148,7 @@ export component MainWindow inherits Window { // Right: chat VerticalLayout { + width: root.width - Theme.rail-width - Theme.sidebar-width; ChatHeader { room-name: root.room-name; topic: root.room-topic; @@ -154,18 +156,21 @@ export component MainWindow inherits Window { encrypted: root.room-encrypted; show-members <=> root.show-members; open-threads => { - root.not-implemented("Threads"); + root.not-implemented(@tr("Threads")); } open-pins => { - root.not-implemented("Pinned messages"); + root.not-implemented(@tr("Pinned messages")); } open-search => { - root.not-implemented("Search"); + root.not-implemented(@tr("Search")); } } - HorizontalLayout { + Rectangle { + vertical-stretch: 1; VerticalLayout { + width: parent.width - (root.show-members ? Theme.member-width : 0px); + height: parent.height; MessageList { messages: root.messages; room-name: root.room-name; @@ -198,8 +203,8 @@ export component MainWindow inherits Window { text-to-set: root.composer-text; text-nonce: root.composer-text-nonce; focus-nonce: root.composer-focus-nonce; - placeholder: root.room-is-dm ? "Message @\{root.room-name}" : "Message #\{root.room-name}"; - banner: root.editing-id != "" ? "Editing message" : (root.reply-to-id != "" ? "Replying to \{root.reply-to-name}" : ""); + placeholder: root.room-is-dm ? @tr("Message @{}", root.room-name) : @tr("Message #{}", root.room-name); + banner: root.editing-id != "" ? @tr("Editing message") : (root.reply-to-id != "" ? @tr("Replying to {}", root.reply-to-name) : ""); editing: root.editing-id != ""; typing-text: root.typing-text; submit(text) => { @@ -221,19 +226,20 @@ export component MainWindow inherits Window { root.typing(); } attach => { - root.not-implemented("File uploads"); + root.not-implemented(@tr("File uploads")); } } } if root.show-members: MemberList { + x: parent.width - self.width; + height: parent.height; members: root.members; } } } } - // Transient banner for errors and "coming soon" notices. if root.toast != "": Rectangle { width: min(480px, parent.width - 32px); height: 40px;