feat: single instance support via dbus activation

This commit is contained in:
Ashley Wulber 2023-11-20 15:08:01 -05:00 committed by GitHub
parent 608b2d9219
commit 8b1a0c95f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 343 additions and 128 deletions

View file

@ -1,6 +1,7 @@
// Copyright 2023 System76 <info@system76.com>
// SPDX-License-Identifier: GPL-3.0-only
use cosmic::app::DbusActivationDetails;
use cosmic::iced::Subscription;
use cosmic::{
app::{Command, Core},
@ -15,8 +16,10 @@ use cosmic::{
};
use cosmic_panel_config::CosmicPanelConfig;
use cosmic_settings_page::{self as page, section};
use page::Entity;
use crate::config::Config;
use crate::PageCommands;
use crate::pages::desktop::appearance::COLOR_PICKER_DIALOG_ID;
use crate::pages::desktop::{
@ -46,6 +49,20 @@ pub struct SettingsApp {
search_selections: Vec<(page::Entity, section::Entity)>,
}
impl SettingsApp {
fn subcommand_to_page(&self, cmd: PageCommands) -> Option<Entity> {
match cmd {
// PageCommands::Bluetooth => self.pages.page_id::<system::bluetooth::Page>(),
// PageCommands::Network => self.pages.page_id::<system::network::Page>(),
// PageCommands::Notifications => self.pages.page_id::<notifications::Page>(),
// PageCommands::Power => self.pages.page_id::<system::power::Page>(),
PageCommands::Sound => self.pages.page_id::<sound::Page>(),
PageCommands::Time => self.pages.page_id::<time::Page>(),
_ => None,
}
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub enum Message {
@ -58,11 +75,18 @@ pub enum Message {
OpenContextDrawer(Cow<'static, str>),
CloseContextDrawer,
SetTheme(cosmic::theme::Theme),
DbusActivation(DbusActivationDetails<PageCommands, Vec<String>>),
}
impl From<DbusActivationDetails<PageCommands, Vec<String>>> for Message {
fn from(msg: DbusActivationDetails<PageCommands, Vec<String>>) -> Self {
Message::DbusActivation(msg)
}
}
impl cosmic::Application for SettingsApp {
type Executor = cosmic::executor::single::Executor;
type Flags = ();
type Flags = crate::Args;
type Message = Message;
const APP_ID: &'static str = "com.system76.CosmicSettings";
@ -75,7 +99,7 @@ impl cosmic::Application for SettingsApp {
&mut self.core
}
fn init(core: Core, _flags: Self::Flags) -> (Self, Command<Self::Message>) {
fn init(core: Core, flags: Self::Flags) -> (Self, Command<Self::Message>) {
let mut app = SettingsApp {
active_page: page::Entity::default(),
config: Config::new(),
@ -92,10 +116,14 @@ impl cosmic::Application for SettingsApp {
app.insert_page::<time::Page>();
app.insert_page::<input::Page>();
let active_id = app
.pages
.find_page_by_id(&app.config.active_page)
.map_or(desktop_id, |(id, _info)| id);
let active_id = match flags.subcommand {
Some(p) => app.subcommand_to_page(p),
None => app
.pages
.find_page_by_id(&app.config.active_page)
.map(|(id, _info)| id),
}
.unwrap_or(desktop_id);
let command = app.activate_page(active_id);
@ -321,6 +349,18 @@ impl cosmic::Application for SettingsApp {
Message::CloseContextDrawer => {
self.core.window.show_context = false;
}
Message::DbusActivation(msg) => {
let mut cmds = Vec::with_capacity(1);
// if action was passed, use it to change the page
if let DbusActivationDetails::ActivateAction { action, .. } = msg {
if let Some(p) = self.subcommand_to_page(action) {
cmds.push(self.activate_page(p));
}
}
return Command::batch(cmds);
}
}
Command::none()

View file

@ -7,6 +7,8 @@
#![allow(clippy::cast_lossless)]
pub mod app;
use std::str::FromStr;
pub use app::{Message, SettingsApp};
pub mod config;
@ -18,10 +20,60 @@ pub mod widget;
pub mod subscription;
use cosmic::iced::Limits;
use clap::{Parser, Subcommand};
use cosmic::{app::CosmicFlags, iced::Limits};
use i18n_embed::DesktopLanguageRequester;
use ron::error::SpannedError;
use serde::{Deserialize, Serialize};
use tracing_subscriber::prelude::*;
#[derive(Parser, Debug, Serialize, Deserialize, Clone)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Args {
#[command(subcommand)]
subcommand: Option<PageCommands>,
}
#[derive(Subcommand, Debug, Serialize, Deserialize, Clone)]
pub enum PageCommands {
/// Open the settings bluetooth page
Bluetooth,
/// Open the settings network page
Network,
/// Open the settings notifications page
Notifications,
/// Open the settings power page
Power,
/// Open the settings sound page
Sound,
/// Open the settings time page
Time,
}
impl FromStr for PageCommands {
type Err = SpannedError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
ron::de::from_str(s)
}
}
impl ToString for PageCommands {
fn to_string(&self) -> String {
ron::ser::to_string(self).unwrap()
}
}
impl CosmicFlags for Args {
type SubCommand = PageCommands;
type Args = Vec<String>;
fn action(&self) -> Option<&PageCommands> {
self.subcommand.as_ref()
}
}
/// # Errors
///
/// Returns error if iced fails to run the application.
@ -35,10 +87,12 @@ pub fn main() -> color_eyre::Result<()> {
init_logger();
init_localizer();
let args = Args::parse();
let settings = cosmic::app::Settings::default()
.size_limits(Limits::NONE.min_width(400.0).min_height(300.0));
cosmic::app::run::<app::SettingsApp>(settings, ())?;
cosmic::app::run_single_instance::<app::SettingsApp>(settings, args)?;
Ok(())
}

View file

@ -1040,7 +1040,7 @@ pub fn mode_and_colors() -> Section<crate::pages::Message> {
.height(Length::Fixed(100.0))
)
.style(button::Style::Image)
.padding([0, 0])
.padding([8, 0])
.selected(page.theme_mode.is_dark)
.on_press(Message::DarkMode(true)),
text(&descriptions[14])
@ -1415,19 +1415,6 @@ fn color_picker_window_settings() -> SctkWindowSettings {
}
}
// TODO replace with image button / toggle buttons
// fn color_button_<'a, Message: 'a>(
// on_press: Option<Message>,
// color: cosmic::iced::Color,
// selected: bool,
// ) -> cosmic::widget::Button<'a, Message, cosmic::Renderer> {
// let ret = button(cosmic::widget::vertical_space(Length::Fixed(48.0)))
// .width(Length::Fixed(48.0))
// .height(Length::Fixed(48.0))
// .on_press_maybe(on_press);
// color_button(ret, color, selected)
// }
/// A button for selecting a color or gradient.
pub fn color_button<'a, Message: 'a + Clone>(
on_press: Option<Message>,

View file

@ -7,6 +7,7 @@ use cosmic::widget::{
};
use cosmic::{
cctk::sctk::reexports::client::protocol::wl_data_device_manager::DndAction,
cosmic_config::{Config, CosmicConfigEntry},
iced::{
alignment::{Horizontal, Vertical},
@ -33,7 +34,6 @@ use cosmic::{
},
graphics::image::image_rs::EncodableLayout,
},
sctk::reexports::client::protocol::wl_data_device_manager::DndAction,
theme, Apply, Element,
};
@ -1124,18 +1124,17 @@ where
)
},
)));
let data = match &state.dragging_state {
DraggingState::Dragging(a) => Some(a.clone()),
_ => {
shell.publish((self.on_dnd_command_produced.as_ref())(Box::new(
let data = if let DraggingState::Dragging(a) = &state.dragging_state {
Some(a.clone())
} else {
shell.publish((self.on_dnd_command_produced.as_ref())(Box::new(
move || {
platform_specific::wayland::data_device::ActionInner::RequestDndData(
MIME_TYPE.to_string(),
)
},
)));
None
}
None
};
DndOfferState::HandlingOffer(
mime_types.clone(),

View file

@ -1,8 +1,8 @@
use cosmic::{
cctk::sctk::reexports::client::{backend::ObjectId, protocol::wl_output::WlOutput, Proxy},
cosmic_config::{self, CosmicConfigEntry},
iced::Length,
iced_widget::slider,
sctk::reexports::client::{backend::ObjectId, protocol::wl_output::WlOutput, Proxy},
theme,
widget::{
button, container, dropdown, horizontal_space, icon, list, row, settings, text, toggler,