feat: single instance support via dbus activation
This commit is contained in:
parent
608b2d9219
commit
8b1a0c95f0
8 changed files with 343 additions and 128 deletions
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue