streamdeck-workspace/streamdeck-gtk/src/main_window.rs
2022-02-05 19:40:46 -06:00

181 lines
6.2 KiB
Rust

use gio::prelude::SettingsExt;
use gtk::{prelude::*, subclass::prelude::*};
use libhandy::prelude::HdyHeaderBarExt;
glib::wrapper! {
pub struct MainWindow(ObjectSubclass<imp::MainWindow>)
@extends libhandy::ApplicationWindow, gtk::ApplicationWindow, gtk::Window, gtk::Bin, gtk::Container, gtk::Widget, gio::ActionMap;
}
impl MainWindow {
pub(crate) fn new() -> Self {
glib::Object::new(&[]).expect("Failed to create main window")
}
pub(crate) fn to_obs(&self) {
let this = imp::MainWindow::from_instance(self);
let inner = this.inner.get().unwrap();
let borrowed = inner.borrow();
if let Some(stack) = borrowed.main_stack.as_ref() {
stack.set_visible_child_name("obs-config");
}
}
pub(crate) fn build_ui(&self) {
self.set_height_request(350);
self.set_width_request(400);
self.set_title("Streamdeck");
if let Some(variant) = crate::saved_state().user_value("window-size") {
if let Some((width, height)) = variant.get::<(i32, i32)>() {
self.set_default_size(width, height);
}
}
if let Some(variant) = crate::saved_state().user_value("window-position") {
if let Some((x, y)) = variant.get::<(i32, i32)>() {
if x != -1 || y != -1 {
self.move_(x, y)
}
}
}
if crate::saved_state().boolean("window-maximized") {
self.maximize();
}
let main_stack = gtk::Stack::new();
main_stack.add_titled(&crate::views::DeckView::new(), "deck-config", "Streamdecks");
main_stack.add_titled(&crate::views::Obs::new(), "obs-config", "OBS");
let stack_switcher = gtk::StackSwitcher::new();
stack_switcher.set_margin(12);
stack_switcher.set_halign(gtk::Align::Center);
stack_switcher.set_homogeneous(true);
stack_switcher.set_stack(Some(&main_stack));
let headerbar = libhandy::HeaderBar::new();
headerbar.set_show_close_button(true);
headerbar.set_title(Some("Streamdeck"));
headerbar.show_all();
let grid = gtk::Grid::new();
grid.attach(&headerbar, 0, 0, 1, 1);
grid.attach(&stack_switcher, 0, 1, 1, 1);
grid.attach(&main_stack, 0, 2, 1, 1);
grid.show_all();
self.add(&grid);
self.show_all();
let this = imp::MainWindow::from_instance(self);
let inner = this.inner.get().unwrap();
inner.borrow_mut().main_stack = Some(main_stack);
}
}
mod imp {
use gio::prelude::SettingsExt;
use gtk::{prelude::*, subclass::prelude::*};
use once_cell::unsync::OnceCell;
use std::{cell::RefCell, collections::HashMap, rc::Rc};
#[derive(Default, Debug)]
pub(super) struct WindowInner {
pub(super) actions: HashMap<String, gio::Action>,
pub(super) main_stack: Option<gtk::Stack>,
pub(super) configure_id: Option<glib::source::SourceId>,
}
#[derive(Default)]
pub struct MainWindow {
pub(super) inner: OnceCell<Rc<RefCell<WindowInner>>>,
}
#[glib::object_subclass]
impl ObjectSubclass for MainWindow {
const NAME: &'static str = "MainWindow";
type Type = super::MainWindow;
type ParentType = libhandy::ApplicationWindow;
}
impl ObjectImpl for MainWindow {
fn constructed(&self, obj: &Self::Type) {
self.parent_constructed(obj);
self.inner
.set(Rc::new(RefCell::new(WindowInner {
actions: HashMap::new(),
main_stack: None,
configure_id: None,
})))
.unwrap();
}
}
impl ActionMapImpl for MainWindow {
fn lookup_action(&self, _: &Self::Type, name: &str) -> Option<gio::Action> {
let inner = self.inner.get().unwrap();
inner
.borrow()
.actions
.get(name)
.map(|action| action.clone())
}
fn add_action(&self, _: &Self::Type, action: &gio::Action) {
let inner = self.inner.get().unwrap();
inner
.borrow_mut()
.actions
.insert(action.name().into(), action.clone());
}
fn remove_action(&self, _: &Self::Type, name: &str) {
let inner = self.inner.get().unwrap();
inner.borrow_mut().actions.remove(name);
}
}
impl WidgetImpl for MainWindow {
fn configure_event(&self, obj: &Self::Type, event: &gdk::EventConfigure) -> gtk::Inhibit {
let inner = self.inner.get().unwrap();
if inner.borrow().configure_id.is_none() {
let new_configure_id = glib::source::timeout_add_local(
std::time::Duration::from_millis(200),
glib::clone!(@weak-allow-none obj => move || {
if let Some(obj) = obj {
let this = MainWindow::from_instance(&obj);
let inner = this.inner.get().unwrap();
inner.borrow_mut().configure_id = None;
let saved_state = crate::saved_state();
let _ = saved_state.set_boolean("window-maximized", obj.is_maximized());
if !obj.is_maximized() {
let _ = saved_state
.set_value("window-position", &obj.position().to_variant());
let _ =
saved_state.set_value("window-size", &obj.size().to_variant());
}
}
glib::source::Continue(false)
}),
);
inner.borrow_mut().configure_id = Some(new_configure_id);
}
self.parent_configure_event(obj, event)
}
}
impl ContainerImpl for MainWindow {}
impl BinImpl for MainWindow {}
impl WindowImpl for MainWindow {}
impl ApplicationWindowImpl for MainWindow {}
impl libhandy::subclass::prelude::HdyApplicationWindowImpl for MainWindow {}
}