chore: add cosmic app list and cosmic panel buttons
This commit is contained in:
parent
853caa5b84
commit
464338585c
54 changed files with 3855 additions and 187 deletions
28
applets/cosmic-panel-button/src/apps_window/imp.rs
Normal file
28
applets/cosmic-panel-button/src/apps_window/imp.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
|
||||
use gtk4::{glib, subclass::prelude::*};
|
||||
// Object holding the state
|
||||
#[derive(Default)]
|
||||
|
||||
pub struct CosmicPanelAppButtonWindow {}
|
||||
|
||||
// The central trait for subclassing a GObject
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for CosmicPanelAppButtonWindow {
|
||||
// `NAME` needs to match `class` attribute of template
|
||||
const NAME: &'static str = "CosmicPanelAppButtonWindow";
|
||||
type Type = super::CosmicPanelAppButtonWindow;
|
||||
type ParentType = gtk4::ApplicationWindow;
|
||||
}
|
||||
|
||||
// Trait shared by all GObjects
|
||||
impl ObjectImpl for CosmicPanelAppButtonWindow {}
|
||||
|
||||
// Trait shared by all widgets
|
||||
impl WidgetImpl for CosmicPanelAppButtonWindow {}
|
||||
|
||||
// Trait shared by all windows
|
||||
impl WindowImpl for CosmicPanelAppButtonWindow {}
|
||||
|
||||
// Trait shared by all application
|
||||
impl ApplicationWindowImpl for CosmicPanelAppButtonWindow {}
|
||||
71
applets/cosmic-panel-button/src/apps_window/mod.rs
Normal file
71
applets/cosmic-panel-button/src/apps_window/mod.rs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
|
||||
use crate::fl;
|
||||
use cascade::cascade;
|
||||
use cosmic_panel_config::config::CosmicPanelConfig;
|
||||
use gtk4::{
|
||||
gio::{self, DesktopAppInfo, Icon},
|
||||
glib::{self, Object},
|
||||
prelude::*,
|
||||
Align, Application, Button, Orientation,
|
||||
};
|
||||
use std::process::Command;
|
||||
|
||||
mod imp;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct CosmicPanelAppButtonWindow(ObjectSubclass<imp::CosmicPanelAppButtonWindow>)
|
||||
@extends gtk4::ApplicationWindow, gtk4::Window, gtk4::Widget,
|
||||
@implements gio::ActionGroup, gio::ActionMap, gtk4::Accessible, gtk4::Buildable,
|
||||
gtk4::ConstraintTarget, gtk4::Native, gtk4::Root, gtk4::ShortcutManager;
|
||||
}
|
||||
|
||||
impl CosmicPanelAppButtonWindow {
|
||||
pub fn new(app: &Application, app_desktop_file_name: &str) -> Self {
|
||||
let self_: Self = Object::new(&[("application", app)])
|
||||
.expect("Failed to create `CosmicPanelButtonWindow`.");
|
||||
cascade! {
|
||||
&self_;
|
||||
..set_width_request(1);
|
||||
..set_height_request(1);
|
||||
..set_decorated(false);
|
||||
..set_resizable(false);
|
||||
..set_title(Some(app_desktop_file_name));
|
||||
..add_css_class("root_window");
|
||||
};
|
||||
|
||||
if let Some(apps_desktop_info) = DesktopAppInfo::new(&format!("{}.desktop", app_desktop_file_name)) {
|
||||
let app_button = cascade! {
|
||||
Button::new();
|
||||
..add_css_class("apps");
|
||||
};
|
||||
let config = CosmicPanelConfig::load_from_env().unwrap_or_default();
|
||||
let icon = apps_desktop_info.icon().unwrap_or_else(|| {
|
||||
Icon::for_string("image-missing").expect("Failed to set default icon")
|
||||
});
|
||||
let container = gtk4::Box::new(Orientation::Horizontal, 0);
|
||||
let image = cascade! {
|
||||
gtk4::Image::from_gicon(&icon);
|
||||
..set_hexpand(true);
|
||||
..set_halign(Align::Center);
|
||||
..set_pixel_size(config.get_applet_icon_size().try_into().unwrap());
|
||||
..set_tooltip_text(Some(&apps_desktop_info.name()));
|
||||
};
|
||||
container.append(&image);
|
||||
|
||||
app_button.set_child(Some(&container));
|
||||
dbg!(apps_desktop_info.string("Exec").unwrap().as_str());
|
||||
app_button.connect_clicked(move |_| {
|
||||
let _ = Command::new("xdg-shell-wrapper")
|
||||
.env_remove("WAYLAND_SOCKET")
|
||||
.arg(apps_desktop_info.string("Exec").unwrap().as_str())
|
||||
.spawn();
|
||||
});
|
||||
self_.set_child(Some(&app_button));
|
||||
} else {
|
||||
panic!("Requested application is not installed");
|
||||
}
|
||||
|
||||
self_
|
||||
}
|
||||
}
|
||||
38
applets/cosmic-panel-button/src/localize.rs
Normal file
38
applets/cosmic-panel-button/src/localize.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
|
||||
use i18n_embed::{
|
||||
fluent::{fluent_language_loader, FluentLanguageLoader},
|
||||
DefaultLocalizer, LanguageLoader, Localizer,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "i18n/"]
|
||||
struct Localizations;
|
||||
|
||||
pub static LANGUAGE_LOADER: Lazy<FluentLanguageLoader> = Lazy::new(|| {
|
||||
let loader: FluentLanguageLoader = fluent_language_loader!();
|
||||
|
||||
loader
|
||||
.load_fallback_language(&Localizations)
|
||||
.expect("Error while loading fallback language");
|
||||
|
||||
loader
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! fl {
|
||||
($message_id:literal) => {{
|
||||
i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id)
|
||||
}};
|
||||
|
||||
($message_id:literal, $($args:expr),*) => {{
|
||||
i18n_embed_fl::fl!($crate::localize::LANGUAGE_LOADER, $message_id, $($args), *)
|
||||
}};
|
||||
}
|
||||
|
||||
// Get the `Localizer` to be used for localizing this library.
|
||||
pub fn localizer() -> Box<dyn Localizer> {
|
||||
Box::from(DefaultLocalizer::new(&*LANGUAGE_LOADER, &Localizations))
|
||||
}
|
||||
64
applets/cosmic-panel-button/src/main.rs
Normal file
64
applets/cosmic-panel-button/src/main.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
|
||||
use apps_window::CosmicPanelAppButtonWindow;
|
||||
use gtk4::gdk::Display;
|
||||
use gtk4::{
|
||||
gio::{self, ApplicationFlags},
|
||||
glib,
|
||||
prelude::*,
|
||||
CssProvider, StyleContext,
|
||||
};
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
mod apps_window;
|
||||
mod localize;
|
||||
mod utils;
|
||||
|
||||
static ID: OnceCell<String> = OnceCell::new();
|
||||
|
||||
pub fn localize() {
|
||||
let localizer = crate::localize::localizer();
|
||||
let requested_languages = i18n_embed::DesktopLanguageRequester::requested_languages();
|
||||
|
||||
if let Err(error) = localizer.select(&requested_languages) {
|
||||
eprintln!("Error while loading language for App List {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
fn load_css() {
|
||||
let provider = CssProvider::new();
|
||||
provider.load_from_data(include_bytes!("style.css"));
|
||||
|
||||
StyleContext::add_provider_for_display(
|
||||
&Display::default().unwrap(),
|
||||
&provider,
|
||||
gtk4::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Initialize logger
|
||||
pretty_env_logger::init();
|
||||
glib::set_application_name("Cosmic Panel App Button");
|
||||
|
||||
localize();
|
||||
gio::resources_register_include!("compiled.gresource").unwrap();
|
||||
let app = gtk4::Application::new(None, ApplicationFlags::default());
|
||||
app.add_main_option("id", glib::Char::from(b'i'), glib::OptionFlags::NONE, glib::OptionArg::String, "id of the launched application", None);
|
||||
app.connect_handle_local_options(|_app, args| {
|
||||
if let Ok(Some(id)) = args.lookup::<String>("id") {
|
||||
ID.set(id).unwrap();
|
||||
-1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
});
|
||||
app.connect_activate(|app| {
|
||||
load_css();
|
||||
let id = ID.get().unwrap().clone();
|
||||
let window = CosmicPanelAppButtonWindow::new(app, &id);
|
||||
|
||||
window.show();
|
||||
});
|
||||
app.run();
|
||||
}
|
||||
28
applets/cosmic-panel-button/src/style.css
Normal file
28
applets/cosmic-panel-button/src/style.css
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
button {
|
||||
border-radius: 12px;
|
||||
transition: 100ms;
|
||||
padding: 4px;
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
outline-color: transparent;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-radius: 12px;
|
||||
transition: 100ms;
|
||||
padding: 4px;
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
outline-color: rgba(255, 255, 255, 0.1);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
image {
|
||||
border-color: transparent;
|
||||
background: transparent;
|
||||
outline-color: transparent;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
window.root_window {
|
||||
background: transparent;
|
||||
}
|
||||
26
applets/cosmic-panel-button/src/utils.rs
Normal file
26
applets/cosmic-panel-button/src/utils.rs
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use gtk4::glib;
|
||||
use std::future::Future;
|
||||
|
||||
pub fn data_path(id: &str) -> PathBuf {
|
||||
let mut path = glib::user_data_dir();
|
||||
path.push(id);
|
||||
std::fs::create_dir_all(&path).expect("Could not create directory.");
|
||||
path.push("data.json");
|
||||
path
|
||||
}
|
||||
|
||||
pub fn thread_context() -> glib::MainContext {
|
||||
glib::MainContext::thread_default().unwrap_or_else(|| glib::MainContext::new())
|
||||
}
|
||||
|
||||
pub fn block_on<F>(future: F) -> F::Output
|
||||
where
|
||||
F: Future,
|
||||
{
|
||||
let ctx = thread_context();
|
||||
ctx.with_thread_default(|| ctx.block_on(future)).unwrap()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue