保存窗口状态
很多时候,我们希望窗口状态在会话之间持续存在。 如果用户调整窗口大小或将其最大化,他们可能会希望下次打开应用程序时,窗口仍保持同样的状态。 GTK 并没有提供这种功能,但幸运的是,手动实现它并不难。 我们基本上需要两个整数(高度 height
和 宽度 width
) 和一个布尔值(is_maximized
)来持久化。 我们已经知道如何通过使用 gio::Settings
来做到这一点。
文件名:listings/saving_window_state/1/org.gtk_rs.SavingWindowState1.gschema.xml
<?xml version="1.0" encoding="utf-8"?>
<schemalist>
<schema id="org.gtk_rs.SavingWindowState1" path="/org/gtk_rs/SavingWindowState1/">
<key name="window-width" type="i">
<default>-1</default>
<summary>Default window width</summary>
</key>
<key name="window-height" type="i">
<default>-1</default>
<summary>Default window height</summary>
</key>
<key name="is-maximized" type="b">
<default>false</default>
<summary>Default window maximized behaviour</summary>
</key>
</schema>
</schemalist>
由于我们不关心中间状态,因此只在构建窗口时加载窗口状态,并在关闭窗口时保存窗口状态。 这可以通过创建自定义窗口来实现。 首先,我们创建一个窗口,并添加用于访问设置和窗口状态的便捷方法。
文件名:listings/saving_window_state/1/custom_window/mod.rs
mod imp;
use gio::Settings;
use glib::Object;
use gtk::prelude::*;
use gtk::subclass::prelude::*;
use gtk::{gio, glib, Application};
use crate::APP_ID;
glib::wrapper! {
pub struct Window(ObjectSubclass<imp::Window>)
@extends gtk::ApplicationWindow, gtk::Window, gtk::Widget,
@implements gio::ActionGroup, gio::ActionMap, gtk::Accessible, gtk::Buildable,
gtk::ConstraintTarget, gtk::Native, gtk::Root, gtk::ShortcutManager;
}
impl Window {
pub fn new(app: &Application) -> Self {
// Create new window
Object::builder().property("application", app).build()
}
fn setup_settings(&self) {
let settings = Settings::new(APP_ID);
self.imp()
.settings
.set(settings)
.expect("`settings` should not be set before calling `setup_settings`.");
}
fn settings(&self) -> &Settings {
self.imp()
.settings
.get()
.expect("`settings` should be set in `setup_settings`.")
}
pub fn save_window_size(&self) -> Result<(), glib::BoolError> {
// Get the size of the window
let size = self.default_size();
// Set the window state in `settings`
self.settings().set_int("window-width", size.0)?;
self.settings().set_int("window-height", size.1)?;
self.settings()
.set_boolean("is-maximized", self.is_maximized())?;
Ok(())
}
fn load_window_size(&self) {
// Get the window state from `settings`
let width = self.settings().int("window-width");
let height = self.settings().int("window-height");
let is_maximized = self.settings().boolean("is-maximized");
// Set the size of the window
self.set_default_size(width, height);
// If the window was maximized when it was closed, maximize it again
if is_maximized {
self.maximize();
}
}
}
我们通过将 "application" 属性传递给
glib::Object::new
来设置该属性。 您甚至可以用这种方法设置多个属性。 在创建新的 GObject 时,这比手动调用 setter 方法要好得多。
实现的结构包含 settings
. 您可以看到,我们将 settings
嵌入到 std::cell::OnceCell
中。 当你知道只需初始化一次值时,这是 RefCell<Option<T>>
的一个很好的替代方法。 我们还重写了 constructed
和 close_request
方法,通过它们来加载或保存窗口状态。
文件名:listings/saving_window_state/1/custom_window/imp.rs
use gio::Settings;
use gtk::subclass::prelude::*;
use gtk::{gio, glib, ApplicationWindow};
use std::cell::OnceCell;
#[derive(Default)]
pub struct Window {
pub settings: OnceCell<Settings>,
}
#[glib::object_subclass]
impl ObjectSubclass for Window {
const NAME: &'static str = "MyGtkAppWindow";
type Type = super::Window;
type ParentType = ApplicationWindow;
}
impl ObjectImpl for Window {
fn constructed(&self) {
self.parent_constructed();
// Load latest window state
let obj = self.obj();
obj.setup_settings();
obj.load_window_size();
}
}
impl WidgetImpl for Window {}
impl WindowImpl for Window {
// Save window state right before the window will be closed
fn close_request(&self) -> glib::Propagation {
// Save window size
self.obj()
.save_window_size()
.expect("Failed to save window state");
// Allow to invoke other event handlers
glib::Propagation::Proceed
}
}
impl ApplicationWindowImpl for Window {}
就是这样! 现在,我们的窗口可以在应用程序会话之间保持状态。