media widget fix

This commit is contained in:
2026-02-03 22:12:45 +01:00
parent a048d7ae7a
commit cddcc96aa9
12 changed files with 369 additions and 48 deletions

View File

@@ -71,10 +71,19 @@ MediaControlWidget::MediaControlWidget()
}
if (!this->currentTrackId.empty()) {
this->mprisController->set_position(this->currentTrackId, new_position_us);
} else {
if (this->playbackStatus != MprisController::PlaybackStatus::Playing) {
this->schedulePauseAfterSeek();
}
} else if (this->playbackStatus == MprisController::PlaybackStatus::Playing) {
this->mprisController->emit_seeked(new_position_us - this->currentPositionUs); // in us
} else {
return;
}
if (this->playbackStatus == MprisController::PlaybackStatus::Playing) {
this->resetSeekTimer(new_position_us);
} else {
this->setCurrentPosition(new_position_us);
}
this->resetSeekTimer(new_position_us);
});
this->bottomContainer.set_orientation(Gtk::Orientation::HORIZONTAL);
@@ -136,15 +145,25 @@ void MediaControlWidget::onSpotifyMprisUpdated(const MprisPlayer2Message &messag
}
this->artistLabel.set_text(artistText);
this->titleLabel.set_text(message.title);
const bool trackChanged = !this->currentTrackId.empty() && this->currentTrackId != message.track_id;
this->currentTrackId = message.track_id;
if (trackChanged) {
this->currentPositionUs = 0;
}
if (auto texture = TextureCacheService::getInstance()->getTexture(message.artwork_url)) {
this->backgroundImage.set_paintable(texture);
}
this->setTotalLength(message.length_ms * 1000);
this->setCurrentPosition(0);
this->resetSeekTimer(0);
this->setCurrentPosition(this->currentPositionUs);
if (this->playbackStatus == MprisController::PlaybackStatus::Playing) {
this->resetSeekTimer(this->currentPositionUs);
} else if (seekTimerConnection.connected()) {
seekTimerConnection.disconnect();
}
}
void MediaControlWidget::setCurrentPosition(int64_t position_us) {
@@ -181,6 +200,14 @@ void MediaControlWidget::resetSeekTimer(int64_t start_position_us) {
1000);
}
void MediaControlWidget::schedulePauseAfterSeek() {
Glib::signal_timeout().connect_once([this]() {
if (this->playbackStatus != MprisController::PlaybackStatus::Playing) {
this->mprisController->pause();
}
}, 100);
}
bool MediaControlWidget::onSeekTick() {
if (totalLengthUs <= 0) {
return true;
@@ -195,6 +222,7 @@ bool MediaControlWidget::onSeekTick() {
}
void MediaControlWidget::onRunningStateChanged(MprisController::PlaybackStatus status) {
this->playbackStatus = status;
switch (status) {
case MprisController::PlaybackStatus::Playing:
this->onPlay();
@@ -215,14 +243,14 @@ void MediaControlWidget::onPlay() {
}
void MediaControlWidget::onPause() {
this->playPauseButton.set_label("\u23EF"); // Play symbol
this->playPauseButton.set_label("\u23F5"); // Play symbol
if (seekTimerConnection.connected()) {
seekTimerConnection.disconnect();
}
}
void MediaControlWidget::onStop() {
this->playPauseButton.set_label("\u23EF"); // Play symbol
this->playPauseButton.set_label("\u23F9"); // stop symbol
if (seekTimerConnection.connected()) {
seekTimerConnection.disconnect();
}

View File

@@ -17,7 +17,7 @@
BaseNotification::BaseNotification(uint64_t notificationId, std::shared_ptr<Gdk::Monitor> monitor) {
ensure_notification_css_loaded();
set_default_size(350, -1);
set_default_size(350, 100);
gtk_layer_init_for_window(gobj());
gtk_layer_set_monitor(gobj(), monitor->gobj());
gtk_layer_set_layer(gobj(), GTK_LAYER_SHELL_LAYER_OVERLAY);
@@ -72,7 +72,7 @@ void BaseNotification::startAutoClose(int timeoutMs) {
}
autoCloseRemainingMs = timeoutMs;
autoClosePaused = false;
autoClosePaused = false;
start_auto_close_timeout(timeoutMs);
}
@@ -81,12 +81,12 @@ void BaseNotification::start_auto_close_timeout(int timeoutMs) {
autoCloseConnection.disconnect();
}
autoCloseDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
autoCloseDeadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeoutMs);
autoCloseConnection = Glib::signal_timeout().connect([this]() {
this->signal_close.emit(this->notificationId);
return false; // Don't repeat
},
timeoutMs);
timeoutMs);
}
void BaseNotification::pause_auto_close() {
@@ -98,10 +98,10 @@ void BaseNotification::pause_auto_close() {
autoCloseConnection.disconnect();
}
auto now = std::chrono::steady_clock::now();
auto remaining = std::chrono::duration_cast<std::chrono::milliseconds>(autoCloseDeadline - now).count();
auto now = std::chrono::steady_clock::now();
auto remaining = std::chrono::duration_cast<std::chrono::milliseconds>(autoCloseDeadline - now).count();
autoCloseRemainingMs = static_cast<int>(std::max<int64_t>(0, remaining));
autoClosePaused = true;
autoClosePaused = true;
}
void BaseNotification::resume_auto_close() {
@@ -118,7 +118,6 @@ void BaseNotification::resume_auto_close() {
start_auto_close_timeout(autoCloseRemainingMs);
}
void BaseNotification::ensure_notification_css_loaded() {
static bool loaded = false;
if (loaded) {

View File

@@ -0,0 +1,162 @@
#include "widgets/notification/copyNotification.hpp"
#include <memory>
#include <spdlog/spdlog.h>
#include "glibmm/datetime.h"
#include "glibmm/fileutils.h"
#include "glibmm/miscutils.h"
#include "gtkmm/box.h"
#include "gtkmm/button.h"
#include "gtkmm/image.h"
#include "gtkmm/label.h"
// todo refactor out later
void copyToClipboard(const std::string &text) {
auto clipboard = Gdk::Display::get_default()->get_clipboard();
clipboard->set_text(text);
}
void copyToClipboard(const Glib::RefPtr<Gdk::Pixbuf> &image) {
auto clipboard = Gdk::Display::get_default()->get_clipboard();
// Wrap the pixbuf in a Value so GTK knows how to handle it
Glib::Value<Glib::RefPtr<Gdk::Pixbuf>> value;
value.init(Glib::Value<Glib::RefPtr<Gdk::Pixbuf>>::value_type());
value.set(image);
// Create a content provider from the value
auto provider = Gdk::ContentProvider::create(value);
spdlog::info("Copying image to clipboard with content provider");
clipboard->set_content(provider);
}
void createDirectoryIfNotExists(const std::string &dirpath) {
if (!Glib::file_test(dirpath, Glib::FileTest::IS_DIR)) {
try {
g_mkdir_with_parents(dirpath.c_str(), 0755);
} catch (const Glib::Error &e) {
spdlog::error("Failed to create directory {}: {}", dirpath, e.what());
}
}
}
void saveImageToFile(const Glib::RefPtr<Gdk::Pixbuf> &image, const std::string &filepath, const std::string &filename) {
try {
createDirectoryIfNotExists(filepath);
std::string fullpath = filepath + "/" + filename;
image->save(fullpath, "png");
} catch (const Glib::Error &e) {
spdlog::error("Failed to save image to file {}: {}", filepath, e.what());
}
}
CopyNotification::CopyNotification(uint64_t id,
std::shared_ptr<Gdk::Monitor> monitor,
NotifyMessage notify)
: BaseNotification(id, monitor) {
this->set_child(this->mainBox);
this->mainBox.set_orientation(Gtk::Orientation::VERTICAL);
if (notify.imageData) {
this->createImageNotification(notify);
} else if (!notify.body.empty()) {
this->createTextNotification(notify);
} else {
spdlog::warn("CopyNotification created without valid image or text data.");
}
}
void CopyNotification::createImageNotification(NotifyMessage notify) {
this->setupTitle("image copied");
this->type = CopyType::IMAGE;
this->copiedImage = notify.imageData.value();
auto contentBox = Gtk::make_managed<Gtk::Box>();
contentBox->set_orientation(Gtk::Orientation::VERTICAL);
contentBox->set_margin(0);
auto imageWidget = Gtk::make_managed<Gtk::Image>(this->copiedImage);
contentBox->append(*imageWidget);
imageWidget->set_pixel_size(300);
auto buttonBox = Gtk::make_managed<Gtk::Box>();
buttonBox->set_spacing(10);
// material icons unicode
auto saveToClipboardLabel = Gtk::make_managed<Gtk::Label>("\uF0EA"); // content copy icon
auto saveToFileLabel = Gtk::make_managed<Gtk::Label>("\uF1C5"); // save icon
auto saveToClipboardButton = Gtk::make_managed<Gtk::Button>();
saveToClipboardButton->set_child(*saveToClipboardLabel);
saveToClipboardButton->signal_clicked().connect([this]() {
copyToClipboard(this->copiedImage);
spdlog::info("Copied image to clipboard");
this->signal_close.emit(this->notificationId);
});
saveToClipboardButton->set_tooltip_text("Copy to Clipboard");
saveToClipboardButton->add_css_class("notification-button");
saveToClipboardButton->add_css_class("notification-icon-button");
auto saveToFileButton = Gtk::make_managed<Gtk::Button>();
saveToFileButton->set_child(*saveToFileLabel);
saveToFileButton->signal_clicked().connect([this]() {
// xdg-pic/screenshot // use env
auto xdgPicturesDir = Glib::get_user_special_dir(Glib::UserDirectory::PICTURES);
auto dateStamp = Glib::DateTime::create_now_local().format("%Y%m%d_%H%M%S");
auto filepath = xdgPicturesDir + "/screenshot" ;
auto filename = dateStamp + ".png";
saveImageToFile(this->copiedImage, filepath, filename);
spdlog::info("Saved image to {}", filepath.c_str());
this->signal_close.emit(this->notificationId);
});
saveToFileButton->set_tooltip_text("Save to File");
saveToFileButton->add_css_class("notification-button");
saveToFileButton->add_css_class("notification-icon-button");
buttonBox->append(*saveToClipboardButton);
buttonBox->append(*saveToFileButton);
contentBox->append(*buttonBox);
this->mainBox.append(*contentBox);
}
void CopyNotification::createTextNotification(NotifyMessage notify) {
this->setupTitle("text copied");
this->type = CopyType::TEXT;
this->copiedText = notify.body;
auto contentBox = Gtk::make_managed<Gtk::Box>();
contentBox->set_orientation(Gtk::Orientation::VERTICAL);
auto textLabel = Gtk::make_managed<Gtk::Label>(this->copiedText);
textLabel->set_margin_bottom(10);
contentBox->append(*textLabel);
auto copyToClipboardButton = Gtk::make_managed<Gtk::Button>();
auto copyToClipboardLabel = Gtk::make_managed<Gtk::Label>("\uF0EA"); // content copy icon
copyToClipboardButton->set_child(*copyToClipboardLabel);
copyToClipboardButton->signal_clicked().connect([this]() {
copyToClipboard(this->copiedText);
this->signal_close.emit(this->notificationId);
});
copyToClipboardButton->set_tooltip_text("Copy to Clipboard");
copyToClipboardButton->add_css_class("notification-icon-button");
copyToClipboardButton->add_css_class("notification-button");
copyToClipboardButton->set_hexpand(false);
copyToClipboardButton->set_halign(Gtk::Align::START);
contentBox->append(*copyToClipboardButton);
this->mainBox.append(*contentBox);
}
void CopyNotification::setupTitle(std::string title) {
this->title = title;
auto titleLabel = Gtk::make_managed<Gtk::Label>(this->title);
titleLabel->set_margin_bottom(8);
this->mainBox.append(*titleLabel);
}