working timer
This commit is contained in:
@@ -57,8 +57,10 @@ target_sources(bar_lib
|
|||||||
src/widgets/notification/spotifyNotification.cpp
|
src/widgets/notification/spotifyNotification.cpp
|
||||||
src/widgets/controlCenter/controlCenter.cpp
|
src/widgets/controlCenter/controlCenter.cpp
|
||||||
src/widgets/controlCenter/mediaWidget.cpp
|
src/widgets/controlCenter/mediaWidget.cpp
|
||||||
|
src/widgets/controlCenter/timer.cpp
|
||||||
src/widgets/volumeWidget.cpp
|
src/widgets/volumeWidget.cpp
|
||||||
src/widgets/weather.cpp
|
src/widgets/weather.cpp
|
||||||
|
src/widgets/wallpaperWindow.cpp
|
||||||
src/widgets/webWidget.cpp
|
src/widgets/webWidget.cpp
|
||||||
src/widgets/tray.cpp
|
src/widgets/tray.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "connection/dbus/notification.hpp"
|
#include "connection/dbus/notification.hpp"
|
||||||
#include "connection/dbus/tray.hpp"
|
#include "connection/dbus/tray.hpp"
|
||||||
#include "services/hyprland.hpp"
|
#include "services/hyprland.hpp"
|
||||||
|
#include "widgets/wallpaperWindow.hpp"
|
||||||
|
|
||||||
#include "glibmm/refptr.h"
|
#include "glibmm/refptr.h"
|
||||||
#include "gtkmm/application.h"
|
#include "gtkmm/application.h"
|
||||||
@@ -21,6 +22,7 @@ class App {
|
|||||||
private:
|
private:
|
||||||
Glib::RefPtr<Gtk::Application> app;
|
Glib::RefPtr<Gtk::Application> app;
|
||||||
std::vector<std::shared_ptr<Bar>> bars;
|
std::vector<std::shared_ptr<Bar>> bars;
|
||||||
|
std::vector<std::shared_ptr<WallpaperWindow>> wallpaperWindows;
|
||||||
std::shared_ptr<NotificationService> notificationService = nullptr;
|
std::shared_ptr<NotificationService> notificationService = nullptr;
|
||||||
std::shared_ptr<MprisController> mprisController = nullptr;
|
std::shared_ptr<MprisController> mprisController = nullptr;
|
||||||
HyprlandService *hyprlandService = nullptr;
|
HyprlandService *hyprlandService = nullptr;
|
||||||
|
|||||||
105
include/components/timer.hpp
Normal file
105
include/components/timer.hpp
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "components/button/iconButton.hpp"
|
||||||
|
#include "services/timerService.hpp"
|
||||||
|
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
#include "gtkmm/label.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
std::string format_duration(const std::string &digits) {
|
||||||
|
std::string d = digits;
|
||||||
|
if (d.size() > 6) {
|
||||||
|
d = d.substr(d.size() - 6);
|
||||||
|
}
|
||||||
|
if (d.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string padded = std::string(6 - d.size(), '0') + d;
|
||||||
|
|
||||||
|
int h = std::stoi(padded.substr(0, 2));
|
||||||
|
int m = std::stoi(padded.substr(2, 2));
|
||||||
|
int s = std::stoi(padded.substr(4, 2));
|
||||||
|
|
||||||
|
std::ostringstream out;
|
||||||
|
if (h > 0) {
|
||||||
|
out << h << "h " << std::setw(2) << std::setfill('0') << m << "m "
|
||||||
|
<< std::setw(2) << std::setfill('0') << s << "s";
|
||||||
|
} else if (m > 0) {
|
||||||
|
out << m << "m " << std::setw(2) << std::setfill('0') << s << "s";
|
||||||
|
} else {
|
||||||
|
out << s << "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
class Timer : public Gtk::Box {
|
||||||
|
public:
|
||||||
|
Timer(const std::string &duration, uint64_t timerId) {
|
||||||
|
uint32_t totalTime = std::stoul(duration);
|
||||||
|
u_int64_t seconds = totalTime % 100;
|
||||||
|
u_int64_t minutes = (totalTime / 100) % 100;
|
||||||
|
u_int64_t hours = totalTime / 10000;
|
||||||
|
|
||||||
|
u_int64_t totalSeconds = hours * 3600 + minutes * 60 + seconds + 1; // +1 to account for the first tick happening after 1 second
|
||||||
|
|
||||||
|
this->timeLeft = totalSeconds;
|
||||||
|
this->timerLabel = std::make_shared<Gtk::Label>(format_duration(duration));
|
||||||
|
timerLabel->add_css_class("control-center-timer-label");
|
||||||
|
|
||||||
|
auto timerBox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL, 5);
|
||||||
|
timerBox->append(*timerLabel);
|
||||||
|
|
||||||
|
auto cancelButton = Gtk::make_managed<IconButton>(Icon::Type::TOKEN);
|
||||||
|
cancelButton->set_tooltip_text("Cancel Timer");
|
||||||
|
cancelButton->signal_clicked().connect([this, timerId]() {
|
||||||
|
this->timerService->removeTimer(timerId);
|
||||||
|
});
|
||||||
|
timerBox->append(*cancelButton);
|
||||||
|
this->append(*timerBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
void activateTimer() {
|
||||||
|
std::cout << "Timer activated" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateTimeLeft(uint64_t timeValue) {
|
||||||
|
uint64_t s = timeValue % 100;
|
||||||
|
uint64_t m = (timeValue / 100) % 100;
|
||||||
|
uint64_t h = timeValue / 10000;
|
||||||
|
uint64_t totalSeconds = h * 3600 + m * 60 + s;
|
||||||
|
this->timeLeft = totalSeconds;
|
||||||
|
|
||||||
|
std::ostringstream out;
|
||||||
|
if (h > 0) {
|
||||||
|
out << h << "h " << std::setw(2) << std::setfill('0') << m << "m "
|
||||||
|
<< std::setw(2) << std::setfill('0') << s << "s";
|
||||||
|
} else if (m > 0) {
|
||||||
|
out << m << "m " << std::setw(2) << std::setfill('0') << s << "s";
|
||||||
|
} else {
|
||||||
|
out << s << "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
this->timerLabel->set_text(out.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tickDown() {
|
||||||
|
if (timeLeft > 0) {
|
||||||
|
this->updateTimeLeft(this->timeLeft - 1);
|
||||||
|
} else {
|
||||||
|
timeLeft = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<TimerService> timerService = TimerService::getInstance();
|
||||||
|
uint64_t timeLeft;
|
||||||
|
std::shared_ptr<Gtk::Label> timerLabel;
|
||||||
|
};
|
||||||
83
include/services/timerService.hpp
Normal file
83
include/services/timerService.hpp
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "glibmm/main.h"
|
||||||
|
#include "sigc++/signal.h"
|
||||||
|
class TimerService {
|
||||||
|
struct TimerData {
|
||||||
|
uint64_t duration;
|
||||||
|
uint64_t timerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::shared_ptr<TimerService> getInstance() {
|
||||||
|
if (!instance) {
|
||||||
|
instance = std::make_shared<TimerService>();
|
||||||
|
|
||||||
|
// add a timer to tick every second
|
||||||
|
Glib::signal_timeout().connect([]() {
|
||||||
|
instance->tick();
|
||||||
|
return true; // continue calling every second
|
||||||
|
},
|
||||||
|
1000);
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTimer(uint64_t duration) {
|
||||||
|
this->timerAddedSignal.emit(std::to_string(duration), timerIdCounter);
|
||||||
|
|
||||||
|
this->activeTimers[timerIdCounter] = TimerData{.duration = duration, .timerId = timerIdCounter};
|
||||||
|
|
||||||
|
timerIdCounter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void expireTimer(uint64_t timerId) {
|
||||||
|
this->timerExpiredSignal.emit(timerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeTimer(uint64_t timerId) {
|
||||||
|
this->activeTimers.erase(timerId);
|
||||||
|
this->timerCancelledSignal.emit(timerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void(const std::string &, u_int64_t)> &getSignalTimerSet() {
|
||||||
|
return this->timerAddedSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void(u_int64_t)> &getSignalTimerExpired() {
|
||||||
|
return this->timerExpiredSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void(u_int64_t)> &getSignalTimerCancelled() {
|
||||||
|
return this->timerCancelledSignal;
|
||||||
|
}
|
||||||
|
|
||||||
|
sigc::signal<void()> tickSignal;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static inline uint64_t timerIdCounter = 0;
|
||||||
|
sigc::signal<void(const std::string &, u_int64_t)> timerAddedSignal;
|
||||||
|
sigc::signal<void(u_int64_t)> timerCancelledSignal;
|
||||||
|
sigc::signal<void(u_int64_t)> timerExpiredSignal;
|
||||||
|
inline static std::shared_ptr<TimerService> instance = nullptr;
|
||||||
|
|
||||||
|
std::map<uint64_t, TimerData> activeTimers;
|
||||||
|
|
||||||
|
void tick() {
|
||||||
|
for (auto &[id, timer] : activeTimers) {
|
||||||
|
|
||||||
|
if (timer.duration > 0) {
|
||||||
|
timer.duration--;
|
||||||
|
} else if (timer.duration == 0) {
|
||||||
|
expireTimer(timer.timerId);
|
||||||
|
timer.duration = -1; // Mark as expired to prevent multiple expirations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tickSignal.emit();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "components/button/tabButton.hpp"
|
#include "components/button/tabButton.hpp"
|
||||||
#include "components/popover.hpp"
|
#include "components/popover.hpp"
|
||||||
#include "widgets/controlCenter/mediaWidget.hpp"
|
#include "widgets/controlCenter/mediaWidget.hpp"
|
||||||
|
#include "widgets/controlCenter/timer.hpp"
|
||||||
#include "widgets/weather.hpp"
|
#include "widgets/weather.hpp"
|
||||||
|
|
||||||
#include "gtkmm/box.h"
|
#include "gtkmm/box.h"
|
||||||
@@ -29,6 +30,7 @@ class ControlCenter : public Popover {
|
|||||||
|
|
||||||
std::unique_ptr<WeatherWidget> weatherWidget;
|
std::unique_ptr<WeatherWidget> weatherWidget;
|
||||||
std::unique_ptr<MediaWidget> mediaControlWidget;
|
std::unique_ptr<MediaWidget> mediaControlWidget;
|
||||||
|
std::unique_ptr<TimerWidget> timerWidget;
|
||||||
|
|
||||||
void addPlayerWidget(const std::string &bus_name);
|
void addPlayerWidget(const std::string &bus_name);
|
||||||
void removePlayerWidget(const std::string &bus_name);
|
void removePlayerWidget(const std::string &bus_name);
|
||||||
|
|||||||
22
include/widgets/controlCenter/timer.hpp
Normal file
22
include/widgets/controlCenter/timer.hpp
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include "components/timer.hpp"
|
||||||
|
#include "services/timerService.hpp"
|
||||||
|
#include "gtkmm/box.h"
|
||||||
|
|
||||||
|
class TimerWidget : public Gtk::Box {
|
||||||
|
public:
|
||||||
|
TimerWidget();
|
||||||
|
void addTimer(const std::string &duration, uint64_t timerId);
|
||||||
|
void removeTimer(uint64_t timerId);
|
||||||
|
void activateTimer(uint64_t timerId);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<TimerService> timerService = TimerService::getInstance();
|
||||||
|
bool updatingText = false;
|
||||||
|
std::string rawDigits;
|
||||||
|
|
||||||
|
std::map<uint64_t, std::unique_ptr<Timer>> activeTimers;
|
||||||
|
};
|
||||||
16
include/widgets/wallpaperWindow.hpp
Normal file
16
include/widgets/wallpaperWindow.hpp
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gtk4-layer-shell/gtk4-layer-shell.h>
|
||||||
|
#include <gtkmm.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class WallpaperWindow : public Gtk::Window {
|
||||||
|
public:
|
||||||
|
WallpaperWindow(GdkMonitor *monitor, const std::string &imagePath);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Gtk::Picture picture;
|
||||||
|
|
||||||
|
static std::string expand_user_path(const std::string &path);
|
||||||
|
static Glib::RefPtr<Gdk::Texture> load_texture(const std::string &path);
|
||||||
|
};
|
||||||
@@ -19,6 +19,12 @@ window {
|
|||||||
padding: 2px 6px;
|
padding: 2px 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text-area {
|
||||||
|
font-family: var(--text-font-mono);
|
||||||
|
background-color: rgba(25, 25, 25, 0.8);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
.material-icons {
|
.material-icons {
|
||||||
font-family: var(--icon-font-material);
|
font-family: var(--icon-font-material);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "connection/dbus/notification.hpp"
|
#include "connection/dbus/notification.hpp"
|
||||||
#include "services/notificationController.hpp"
|
#include "services/notificationController.hpp"
|
||||||
#include "services/textureCache.hpp"
|
#include "services/textureCache.hpp"
|
||||||
|
#include "widgets/wallpaperWindow.hpp"
|
||||||
|
|
||||||
App::App() {
|
App::App() {
|
||||||
this->app = Gtk::Application::create("org.example.mybar");
|
this->app = Gtk::Application::create("org.example.mybar");
|
||||||
|
|||||||
@@ -39,15 +39,15 @@ ControlCenter::ControlCenter(Icon::Type icon, std::string name)
|
|||||||
this->contentStack.set_transition_type(Gtk::StackTransitionType::CROSSFADE);
|
this->contentStack.set_transition_type(Gtk::StackTransitionType::CROSSFADE);
|
||||||
|
|
||||||
this->mediaControlWidget = std::make_unique<MediaWidget>();
|
this->mediaControlWidget = std::make_unique<MediaWidget>();
|
||||||
|
|
||||||
this->weatherWidget = std::make_unique<WeatherWidget>();
|
this->weatherWidget = std::make_unique<WeatherWidget>();
|
||||||
|
this->timerWidget = std::make_unique<TimerWidget>();
|
||||||
|
|
||||||
this->contentStack.add(*this->mediaControlWidget, "controls", "Controls");
|
this->contentStack.add(*this->mediaControlWidget, "controls", "Controls");
|
||||||
this->contentStack.add(*this->weatherWidget, "info", "Info");
|
this->contentStack.add(*this->weatherWidget, "info", "Info");
|
||||||
this->contentStack.add(*Gtk::make_managed<Gtk::Label>("Timer"), "timer", "Timer");
|
this->contentStack.add(*this->timerWidget, "timer", "Timer");
|
||||||
|
|
||||||
this->contentStack.set_visible_child("controls");
|
this->contentStack.set_visible_child("controls");
|
||||||
this->setActiveTab("info");
|
this->setActiveTab("controls");
|
||||||
|
|
||||||
this->container.append(this->contentStack);
|
this->container.append(this->contentStack);
|
||||||
|
|
||||||
|
|||||||
125
src/widgets/controlCenter/timer.cpp
Normal file
125
src/widgets/controlCenter/timer.cpp
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
#include "widgets/controlCenter/timer.hpp"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <gdk/gdkkeysyms.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "gtkmm/entry.h"
|
||||||
|
#include "gtkmm/eventcontrollerkey.h"
|
||||||
|
#include "gtkmm/label.h"
|
||||||
|
|
||||||
|
TimerWidget::TimerWidget() {
|
||||||
|
set_orientation(Gtk::Orientation::VERTICAL);
|
||||||
|
set_spacing(6);
|
||||||
|
|
||||||
|
this->timerService->getSignalTimerSet().connect([this](const std::string &duration, uint64_t timerId) {
|
||||||
|
this->addTimer(duration, timerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->timerService->getSignalTimerCancelled().connect([this](uint64_t timerId) {
|
||||||
|
this->removeTimer(timerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->timerService->getSignalTimerExpired().connect([this](uint64_t timerId) {
|
||||||
|
this->activateTimer(timerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
this->timerService->tickSignal.connect([this]() {
|
||||||
|
for (auto &[id, timer] : activeTimers) {
|
||||||
|
timer->tickDown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auto label = Gtk::make_managed<Gtk::Label>("Set Timer");
|
||||||
|
label->add_css_class("control-center-timer-label");
|
||||||
|
|
||||||
|
auto entry = Gtk::make_managed<Gtk::Entry>();
|
||||||
|
entry->set_placeholder_text("0s");
|
||||||
|
entry->set_valign(Gtk::Align::CENTER);
|
||||||
|
entry->set_alignment(0.5);
|
||||||
|
entry->add_css_class("text-area");
|
||||||
|
entry->set_editable(false);
|
||||||
|
entry->set_focusable(true);
|
||||||
|
entry->set_position(-1);
|
||||||
|
|
||||||
|
set_focusable(false);
|
||||||
|
|
||||||
|
auto keyController = Gtk::EventControllerKey::create();
|
||||||
|
keyController->set_propagation_phase(Gtk::PropagationPhase::CAPTURE);
|
||||||
|
keyController->signal_key_pressed().connect([this, entry](guint keyval, guint, Gdk::ModifierType) -> bool {
|
||||||
|
if (updatingText) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyval >= GDK_KEY_0 && keyval <= GDK_KEY_9) {
|
||||||
|
rawDigits.push_back(static_cast<char>('0' + (keyval - GDK_KEY_0)));
|
||||||
|
} else if (keyval >= GDK_KEY_KP_0 && keyval <= GDK_KEY_KP_9) {
|
||||||
|
rawDigits.push_back(static_cast<char>('0' + (keyval - GDK_KEY_KP_0)));
|
||||||
|
} else if (keyval == GDK_KEY_BackSpace || keyval == GDK_KEY_Delete ||
|
||||||
|
keyval == GDK_KEY_KP_Delete) {
|
||||||
|
if (!rawDigits.empty()) {
|
||||||
|
rawDigits.pop_back();
|
||||||
|
}
|
||||||
|
} else if (keyval == GDK_KEY_Return || keyval == GDK_KEY_KP_Enter ||
|
||||||
|
keyval == GDK_KEY_Tab || keyval == GDK_KEY_ISO_Left_Tab) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawDigits.size() > 6) {
|
||||||
|
rawDigits.erase(0, rawDigits.size() - 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatingText = true;
|
||||||
|
entry->set_text(format_duration(rawDigits));
|
||||||
|
entry->set_position(-1);
|
||||||
|
updatingText = false;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
entry->add_controller(keyController);
|
||||||
|
|
||||||
|
entry->signal_activate().connect([this, entry]() {
|
||||||
|
if (rawDigits.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
spdlog::info("Timer set for {} seconds", rawDigits);
|
||||||
|
this->timerService->addTimer(std::stoul(rawDigits));
|
||||||
|
rawDigits.clear();
|
||||||
|
this->updatingText = true;
|
||||||
|
entry->set_text("");
|
||||||
|
entry->set_position(-1);
|
||||||
|
this->updatingText = false;
|
||||||
|
},
|
||||||
|
false);
|
||||||
|
|
||||||
|
append(*label);
|
||||||
|
append(*entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerWidget::addTimer(const std::string &duration, uint64_t timerId) {
|
||||||
|
std::unique_ptr<Timer> timer = std::make_unique<Timer>(duration, timerId);
|
||||||
|
|
||||||
|
append(*timer);
|
||||||
|
|
||||||
|
this->activeTimers[timerId] = std::move(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerWidget::removeTimer(uint64_t timerId) {
|
||||||
|
auto it = this->activeTimers.find(timerId);
|
||||||
|
if (it != this->activeTimers.end()) {
|
||||||
|
this->remove(*it->second);
|
||||||
|
this->activeTimers.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimerWidget::activateTimer(uint64_t timerId) {
|
||||||
|
auto it = this->activeTimers.find(timerId);
|
||||||
|
if (it != this->activeTimers.end()) {
|
||||||
|
it->second->activateTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/widgets/wallpaperWindow.cpp
Normal file
89
src/widgets/wallpaperWindow.cpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
#include "widgets/wallpaperWindow.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
#include <gdk/gdk.h>
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include "giomm/file.h"
|
||||||
|
|
||||||
|
WallpaperWindow::WallpaperWindow(GdkMonitor *monitor, const std::string &imagePath) {
|
||||||
|
set_name("wallpaper-window");
|
||||||
|
set_resizable(false);
|
||||||
|
set_focusable(false);
|
||||||
|
|
||||||
|
gtk_layer_init_for_window(gobj());
|
||||||
|
|
||||||
|
if (monitor) {
|
||||||
|
gtk_layer_set_monitor(gobj(), monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_BACKGROUND);
|
||||||
|
gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_TOP, true);
|
||||||
|
gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_BOTTOM, true);
|
||||||
|
gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_LEFT, true);
|
||||||
|
gtk_layer_set_anchor(gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, true);
|
||||||
|
gtk_layer_set_exclusive_zone(gobj(), 0);
|
||||||
|
gtk_layer_set_keyboard_mode(gobj(), GTK_LAYER_SHELL_KEYBOARD_MODE_NONE);
|
||||||
|
|
||||||
|
picture.set_hexpand(true);
|
||||||
|
picture.set_vexpand(true);
|
||||||
|
picture.set_halign(Gtk::Align::FILL);
|
||||||
|
picture.set_valign(Gtk::Align::FILL);
|
||||||
|
picture.set_can_shrink(true);
|
||||||
|
picture.set_content_fit(Gtk::ContentFit::COVER);
|
||||||
|
|
||||||
|
if (monitor) {
|
||||||
|
GdkRectangle geom{};
|
||||||
|
gdk_monitor_get_geometry(monitor, &geom);
|
||||||
|
set_default_size(geom.width, geom.height);
|
||||||
|
set_size_request(geom.width, geom.height);
|
||||||
|
picture.set_size_request(geom.width, geom.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resolved_path = expand_user_path(imagePath);
|
||||||
|
if (auto texture = load_texture(resolved_path)) {
|
||||||
|
picture.set_paintable(texture);
|
||||||
|
} else {
|
||||||
|
spdlog::warn("Wallpaper image failed to load: {}", resolved_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_child(picture);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WallpaperWindow::expand_user_path(const std::string &path) {
|
||||||
|
if (path.rfind("~/", 0) != 0) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *home = std::getenv("HOME");
|
||||||
|
if (!home || !*home) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::filesystem::path(home) / path.substr(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Glib::RefPtr<Gdk::Texture> WallpaperWindow::load_texture(const std::string &path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
spdlog::warn("Wallpaper image path is empty");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(path)) {
|
||||||
|
spdlog::warn("Wallpaper image not found at path: {}", path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto file = Gio::File::create_for_path(path);
|
||||||
|
return Gdk::Texture::create_from_file(file);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
spdlog::warn("Failed to load wallpaper image {}: {}", path, ex.what());
|
||||||
|
return {};
|
||||||
|
} catch (...) {
|
||||||
|
spdlog::warn("Failed to load wallpaper image {}: unknown error", path);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user