Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src-tauri/src/client_prefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,20 @@ mod tests {
assert_eq!(merged(&p, None)["check_updates"], true);
}

/// 界面大小跟着这台电脑的屏幕走:不交上去,别处交下来的也不盖
#[test]
fn the_interface_size_stays_on_this_computer() {
let big = crate::zoom::Zoom::try_from(150).unwrap();
let p = Prefs {
zoom: big,
..Prefs::default()
};
assert!(!general(&p).contains_key("zoom"));
assert!(same(&Prefs::default(), &p));
let got = overlay(&Prefs::default(), &serde_json::json!({ "zoom": 150 }));
assert_eq!(got.zoom, crate::zoom::Zoom::default());
}

#[test]
fn sameness_only_looks_at_the_shared_settings() {
let a = Prefs::default();
Expand Down
9 changes: 7 additions & 2 deletions src-tauri/src/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,11 @@ pub fn show_picker(app: &tauri::AppHandle, why: launch::Why) -> tauri::Result<()
crate::i18n::init_script(),
reason
))
.inner_size(PICKER_WIDTH, 300.0)
// 按界面大小放大,高度由 `picker_fit` 按内容定(见 `window::fit`)
.inner_size(
PICKER_WIDTH * crate::zoom::current().factor(),
300.0 * crate::zoom::current().factor(),
)
.resizable(false)
.minimizable(false)
.maximizable(false)
Expand All @@ -839,7 +843,8 @@ pub fn show_picker(app: &tauri::AppHandle, why: launch::Why) -> tauri::Result<()
Some(dir) => b.data_directory(dir),
None => b,
};
b.build()?;
let w = b.build()?;
crate::zoom::zoom_new(&w);
Ok(())
}

Expand Down
6 changes: 6 additions & 0 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ pub mod window;
pub mod winreg;
pub mod wire;
pub mod zai;
pub mod zoom;

use control::ControlClient;
use gateway::{
Expand Down Expand Up @@ -259,6 +260,9 @@ pub fn run() {
settings::set_language,
settings::app_theme,
settings::set_theme,
settings::ui_zoom,
settings::set_ui_zoom,
settings::step_ui_zoom,
updater::update_check,
updater::update_show,
updater::update_pending,
Expand Down Expand Up @@ -315,6 +319,8 @@ pub fn run() {
i18n::set(i18n::effective(saved.language));
// 外观在窗口出现之前就设好,不然会先画一帧系统那一档的颜色
theme::init(&handle, saved.theme);
// 界面大小也是:窗口一建出来就按它缩放(见 `window::show_main_window`)
zoom::remember(saved.zoom);
// 链接、开机自启、通知登记改成指向正在运行的这个 exe(见 `winreg`)。
// 要在下面判断系统通知能不能用之前
winreg::claim(&handle);
Expand Down
11 changes: 10 additions & 1 deletion src-tauri/src/prefs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ pub struct Prefs {
pub language: Option<Lang>,
/// 界面外观。`None` 是跟随系统,理由同上。
pub theme: Option<Theme>,
/// 界面大小(整个界面等比缩放,见 `zoom`)。**每台电脑各自的**:合适的大小取决于这台
/// 电脑的屏幕,不同步(不在 `client_prefs` 的列表里)。没写就是 100%
#[serde(default)]
pub zoom: crate::zoom::Zoom,
/// 提醒:系统通知 / 仅在应用内 / 关闭。**只有这一个,不分类。**
pub notices: Mode,
/// 菜单栏上显示什么:标识和数值(出厂)/ 仅标识 / 仅数值 / 不显示。
Expand All @@ -54,6 +58,7 @@ impl Default for Prefs {
check_updates: true,
language: None,
theme: None,
zoom: crate::zoom::Zoom::default(),
notices: Mode::System,
menubar: crate::menubar::Style::Full,
client_locations: BTreeMap::new(),
Expand Down Expand Up @@ -102,6 +107,7 @@ fn from_fields(fields: &Fields) -> Prefs {
check_updates: field(fields, "check_updates").unwrap_or(d.check_updates),
language: field(fields, "language").unwrap_or(d.language),
theme: field(fields, "theme").unwrap_or(d.theme),
zoom: field(fields, "zoom").unwrap_or(d.zoom),
notices: field(fields, "notices").unwrap_or(d.notices),
menubar: field(fields, "menubar").unwrap_or(d.menubar),
// 一个客户端一个客户端地读:读不懂的只丢那一个
Expand Down Expand Up @@ -286,6 +292,7 @@ mod tests {
check_updates: false,
language: Some(Lang::Zh),
theme: Some(Theme::Dark),
zoom: crate::zoom::Zoom::try_from(125).unwrap(),
notices: Mode::App,
menubar: crate::menubar::Style::Numbers,
client_locations: BTreeMap::from([(
Expand Down Expand Up @@ -326,6 +333,7 @@ mod tests {
assert_eq!(p.language, Some(Lang::En));
assert!(!p.check_updates);
assert_eq!(p.menubar, crate::menubar::Style::Full);
assert_eq!(p.zoom, crate::zoom::Zoom::default());
assert!(p.client_locations.is_empty());
}

Expand Down Expand Up @@ -394,13 +402,14 @@ mod tests {
let (_tmp, dir) = tmp();
std::fs::write(
prefs_path(&dir),
br#"{"check_updates":"no","language":"ja","theme":1,"notices":"off","menubar":"icon"}"#,
br#"{"check_updates":"no","language":"ja","theme":1,"zoom":120,"notices":"off","menubar":"icon"}"#,
)
.unwrap();
let p = load(&dir);
assert!(p.check_updates);
assert_eq!(p.language, None);
assert_eq!(p.theme, None);
assert_eq!(p.zoom, crate::zoom::Zoom::default(), "不是那几档的按 100%");
assert_eq!(p.notices, Mode::Off);
assert_eq!(p.menubar, crate::menubar::Style::Icon);
}
Expand Down
49 changes: 47 additions & 2 deletions src-tauri/src/settings.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
//! 设置页:语言、外观、开机启动、提醒方式、菜单栏样式。
//! 设置页:语言、外观、界面大小、开机启动、提醒方式、菜单栏样式。

use std::sync::Arc;

use tauri::{Emitter, Manager};

use crate::{
AppState, autostart, data_dir, error::Out, gateway::locate_core, i18n, menubar, notices, prefs,
theme,
theme, zoom,
};

/// 这个应用自己的信息。
Expand Down Expand Up @@ -104,6 +104,51 @@ pub async fn set_theme(app: tauri::AppHandle, setting: Option<theme::Theme>) ->
Ok(theme_view())
}

/// 界面大小,百分比(见 `zoom`)
#[tauri::command]
pub fn ui_zoom() -> zoom::Zoom {
zoom::current()
}

/// 设置页选一档
#[tauri::command]
pub async fn set_ui_zoom(app: tauri::AppHandle, zoom: zoom::Zoom) -> Out<zoom::Zoom> {
change_zoom(app, move |_| zoom).await
}

/// 快捷键:放大一档、缩小一档、回到 100%。**新的一档在这边算**:按住 ⌘= 不放时一连串
/// 的按键各自读、改、写,在界面那边算的话几次读到的是同一档
#[tauri::command]
pub async fn step_ui_zoom(app: tauri::AppHandle, step: zoom::Step) -> Out<zoom::Zoom> {
change_zoom(app, move |z| z.step(step)).await
}

/// 换一档:存下来、开着的窗口当场换,设置页那一行听 `ui-zoom-changed` 跟着动。
///
/// **不通知同步**(不调 `client_prefs::changed`):界面大小是这台电脑自己的。
///
/// **记下的那一档在存设置的锁里定**(`prefs::update` 一次只改一处):接连几次换档各自
/// 在阻塞线程上存、各自回来应用,回来的先后不一定是存的先后;应用时一律取最后存下的那
/// 一档,先回来的那次应用到的也已经是它
async fn change_zoom(
app: tauri::AppHandle,
f: impl FnOnce(zoom::Zoom) -> zoom::Zoom + Send + 'static,
) -> Out<zoom::Zoom> {
let saved = prefs::change(move |p| {
p.zoom = f(p.zoom);
zoom::remember(p.zoom);
})
.await;
if saved.is_err() {
// 没存上:记下的退回存着的那一档
zoom::remember(prefs::load(&data_dir()).zoom);
}
let now = zoom::current();
zoom::apply(&app, now);
let _ = app.emit("ui-zoom-changed", now);
saved.map(|_| now)
}

/// 开机自启现在是开着的吗。
///
/// **默认是关的,而且这不是「还没实现」,是产品决定。**一个装完就自己
Expand Down
11 changes: 8 additions & 3 deletions src-tauri/src/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,12 @@ pub(crate) fn show_update_window(app: &tauri::AppHandle) -> tauri::Result<()> {
let b = WebviewWindowBuilder::new(app, UPDATE_WINDOW, WebviewUrl::default())
.title(tr!("软件更新", "Software Update"))
.initialization_script(i18n::init_script())
// 高度先随便给一个:网页画完量出内容有多高,再由 `update_fit` 定下来
.inner_size(UPDATE_WIDTH, 220.0)
// 高度先随便给一个:网页画完量出内容有多高,再由 `update_fit` 定下来。
// 宽度按界面大小放大(见 `window::fit`)
.inner_size(
UPDATE_WIDTH * crate::zoom::current().factor(),
220.0 * crate::zoom::current().factor(),
)
.resizable(false)
.minimizable(false)
.maximizable(false)
Expand All @@ -196,7 +200,8 @@ pub(crate) fn show_update_window(app: &tauri::AppHandle) -> tauri::Result<()> {
Some(dir) => b.data_directory(dir),
None => b,
};
b.build()?;
let w = b.build()?;
crate::zoom::zoom_new(&w);
Ok(())
}

Expand Down
33 changes: 31 additions & 2 deletions src-tauri/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,32 @@ pub(crate) fn show_main_window(app: &tauri::AppHandle) -> tauri::Result<()> {
}
state => matches!(state, crate::connection::LinkState::Connected { .. }),
});
// **界面大小**(见 `zoom`):窗口的大小和最小尺寸跟着它放大,放不下的夹在屏幕里。
// 窗口还没建出来,不知道会落在哪块屏幕上,先按主屏算;建好之后按它实际所在的那块
// 再定一次最小尺寸
let zoom = crate::zoom::current();
let room = app
.primary_monitor()
.ok()
.flatten()
.and_then(|m| crate::zoom::room_on(&m));
let (width, height) = crate::zoom::scaled(crate::zoom::MAIN_SIZE, zoom, room);
let (min_width, min_height) = crate::zoom::scaled(crate::zoom::MAIN_MIN, zoom, room);
let b = WebviewWindowBuilder::new(app, "main", WebviewUrl::default())
.title("ThinkWatch Lite")
.initialization_script(format!(
"{} window.__TW_WARM__ = {warm}; window.__TW_VIBRANT__ = {VIBRANT};",
i18n::init_script()
))
.inner_size(1100.0, 720.0)
.min_inner_size(820.0, 560.0)
.inner_size(width, height)
.min_inner_size(min_width, min_height)
.visible(!warm);
// 放大后的窗口占满了屏幕的一边:放在正中,不让系统按层叠的位置摆、一截落到屏幕外
let b = if room.is_some_and(|(rw, rh)| width >= rw || height >= rh) {
b.center()
} else {
b
};
// 绿色版的 WebView2 数据放在它自己的文件夹里(见 `portable::webview_data_dir`)。
// 三扇窗(这里、更新窗口、连接选择)要一致:同一个进程里的 WebView2 共用一个数据目录
let b = match crate::portable::webview_data_dir() {
Expand Down Expand Up @@ -154,6 +171,12 @@ pub(crate) fn show_main_window(app: &tauri::AppHandle) -> tauri::Result<()> {
.build(),
);
let w = b.build()?;
// **建好立刻缩放**,不等网页读设置:冷启动的窗口这时已经在屏幕上了,等网页来设的话,
// 先画出来的是 100% 的那一帧。缩放排在主线程的队列上,页面这时还没开始载入
if zoom != crate::zoom::Zoom::default() {
let _ = w.set_zoom(zoom.factor());
crate::zoom::fit_main_min(&w, zoom);
}
if !warm {
return reveal(app, &w);
}
Expand Down Expand Up @@ -214,7 +237,13 @@ pub(crate) fn reveal(app: &tauri::AppHandle, w: &tauri::WebviewWindow) -> tauri:
/// 标题栏多高不写死 —— 各版本不一样(Tahoe 上是 32 点),没有标题栏的窗
/// 是 0。`contentLayoutRect` 给的正是没被标题栏盖住的那一块,和整扇窗户
/// 一减就是要补上的数。
///
/// **宽高是 CSS 像素**(`width` 是 100% 时的宽度,`height` 是网页量出来的),按界面
/// 大小(见 `zoom`)换成窗口的点:两扇小窗跟着主窗口一起缩放,150% 时 CSS 的 1px 是
/// 1.5 个点。标题栏是系统画的,不跟着缩放,所以只乘网页那一块
pub(crate) fn fit(window: &tauri::Window, width: f64, height: f64) -> tauri::Result<()> {
let z = crate::zoom::current().factor();
let (width, height) = (width * z, height * z);
#[cfg(target_os = "macos")]
{
let w = window.clone();
Expand Down
Loading
Loading