chore: add cosmic app list and cosmic panel buttons

This commit is contained in:
Ashley Wulber 2022-06-06 11:52:45 -04:00
parent 853caa5b84
commit 464338585c
No known key found for this signature in database
GPG key ID: BCD0B777C3F6E2FD
54 changed files with 3855 additions and 187 deletions

View 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 {}

View 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_
}
}

View 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))
}

View 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();
}

View 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;
}

View 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()
}