add move window to hyprland service

This commit is contained in:
2026-02-02 12:23:44 +01:00
parent 9404321249
commit da9f167747
12 changed files with 264 additions and 102 deletions

View File

@@ -54,7 +54,8 @@ target_sources(bar_lib
src/services/dbus/mpris.cpp src/services/dbus/mpris.cpp
src/widgets/tray.cpp src/widgets/tray.cpp
src/widgets/controlCenter.cpp src/widgets/controlCenter/controlCenter.cpp
src/widgets/controlCenter/mediaControl.cpp
src/components/popover.cpp src/components/popover.cpp
src/components/workspaceIndicator.cpp src/components/workspaceIndicator.cpp

View File

@@ -9,7 +9,7 @@
#include "widgets/tray.hpp" #include "widgets/tray.hpp"
#include "widgets/volumeWidget.hpp" #include "widgets/volumeWidget.hpp"
#include "widgets/webWidget.hpp" #include "widgets/webWidget.hpp"
#include "widgets/controlCenter.hpp" #include "widgets/controlCenter/controlCenter.hpp"
class Bar : public Gtk::Window { class Bar : public Gtk::Window {
public: public:

View File

@@ -14,7 +14,7 @@ struct MprisPlayer2Message {
std::string title; std::string title;
std::string artist; std::string artist;
std::string artwork_url; std::string artwork_url;
uint32_t length_ms; int64_t length_ms;
std::function<void()> play_pause; std::function<void()> play_pause;
std::function<void()> next; std::function<void()> next;

View File

@@ -12,12 +12,15 @@ class MprisController {
void toggle_play(); void toggle_play();
void next_song(); void next_song();
void previous_song(); void previous_song();
void emit_seeked(int64_t position_us);
sigc::signal<void(const MprisPlayer2Message &)> &signal_mpris_updated(); sigc::signal<void(const MprisPlayer2Message &)> &signal_mpris_updated();
private: private:
MprisController(); MprisController();
bool playerRunning = false;
Glib::RefPtr<Gio::DBus::Connection> m_connection; Glib::RefPtr<Gio::DBus::Connection> m_connection;
Glib::RefPtr<Gio::DBus::Proxy> m_proxy; Glib::RefPtr<Gio::DBus::Proxy> m_proxy;
sigc::signal<void(const MprisPlayer2Message &)> mprisUpdatedSignal; sigc::signal<void(const MprisPlayer2Message &)> mprisUpdatedSignal;

View File

@@ -67,6 +67,7 @@ class HyprlandService {
ACTIVE_WINDOW, ACTIVE_WINDOW,
OPEN_WINDOW, OPEN_WINDOW,
CLOSE_WINDOW, CLOSE_WINDOW,
MOVE_WINDOW,
URGENT, URGENT,
FOCUSED_MONITOR, FOCUSED_MONITOR,
@@ -78,6 +79,7 @@ class HyprlandService {
{"activewindowv2", ACTIVE_WINDOW}, {"activewindowv2", ACTIVE_WINDOW},
{"openwindow", OPEN_WINDOW}, {"openwindow", OPEN_WINDOW},
{"closewindow", CLOSE_WINDOW}, {"closewindow", CLOSE_WINDOW},
{"movewindowv2", MOVE_WINDOW},
{"urgent", URGENT}, {"urgent", URGENT},
{"focusedmon", FOCUSED_MONITOR}, {"focusedmon", FOCUSED_MONITOR},
{"monitorremoved", MONITOR_REMOVED}, {"monitorremoved", MONITOR_REMOVED},
@@ -86,6 +88,7 @@ class HyprlandService {
void onFocusedMonitorChanged(std::string monitorData); void onFocusedMonitorChanged(std::string monitorData);
void onOpenWindow(std::string windowData); void onOpenWindow(std::string windowData);
void onCloseWindow(std::string windowData); void onCloseWindow(std::string windowData);
void onMoveWindow(std::string windowData);
void onUrgent(std::string windowAddress); void onUrgent(std::string windowAddress);
void onActiveWindowChanged(std::string windowAddress); void onActiveWindowChanged(std::string windowAddress);
void onMonitorRemoved(std::string monitorName); void onMonitorRemoved(std::string monitorName);

View File

@@ -1,46 +0,0 @@
#pragma once
#include <memory>
#include "components/popover.hpp"
#include "gtkmm/box.h"
#include "gtkmm/label.h"
#include "gtkmm/overlay.h"
#include "gtkmm/picture.h"
#include "gtkmm/scale.h"
#include "gtkmm/scrolledwindow.h"
#include "services/dbus/mpris.hpp"
class ControlCenter : public Popover {
public:
ControlCenter(std::string icon, std::string name);
private:
std::shared_ptr<MprisController> mprisController = MprisController::getInstance();
Gtk::Box container;
Gtk::Box spotifyContainer;
// image as background, artist, title
Gtk::Overlay topContainer;
Gtk::Picture backgroundImage;
Gtk::Box infoContainer;
Gtk::Label artistLabel;
Gtk::Label titleLabel;
//
Gtk::Box seekBarContainer;
Gtk::Label currentTimeLabel;
Gtk::Scale seekBar;
Gtk::Label totalTimeLabel;
// playback controls
Gtk::Box bottomContainer;
Gtk::Button previousButton;
Gtk::Button playPauseButton;
Gtk::Button nextButton;
Gtk::ScrolledWindow imageWrapper;
void onSpotifyMprisUpdated(const MprisPlayer2Message &message);
};

View File

@@ -0,0 +1,15 @@
#pragma once
#include "components/popover.hpp"
#include "gtkmm/box.h"
#include "widgets/controlCenter/mediaControl.hpp"
class ControlCenter : public Popover {
public:
ControlCenter(std::string icon, std::string name);
private:
Gtk::Box container;
MediaControlWidget mediaControlWidget;
};

View File

@@ -0,0 +1,52 @@
#pragma once
#include "gtkmm/box.h"
#include "gtkmm/button.h"
#include "gtkmm/label.h"
#include "gtkmm/overlay.h"
#include "gtkmm/picture.h"
#include "gtkmm/scale.h"
#include "gtkmm/scrolledwindow.h"
#include "services/dbus/mpris.hpp"
class MediaControlWidget : public Gtk::Box {
public:
MediaControlWidget();
private:
std::shared_ptr<MprisController> mprisController = MprisController::getInstance();
int64_t currentPositionUs = 0;
int64_t totalLengthUs = 0;
sigc::connection seekTimerConnection;
void setCurrentPosition(int64_t position_us);
void setTotalLength(int64_t length_us);
void resetSeekTimer(int64_t start_position_us);
bool onSeekTick();
Gtk::Box spotifyContainer;
// image as background, artist, title
Gtk::Overlay topContainer;
Gtk::Picture backgroundImage;
Gtk::Box infoContainer;
Gtk::Label artistLabel;
Gtk::Label titleLabel;
//
Gtk::Box seekBarContainer;
Gtk::Label currentTimeLabel;
Gtk::Scale seekBar;
Gtk::Label totalTimeLabel;
// playback controls
Gtk::Box bottomContainer;
Gtk::Button previousButton;
Gtk::Button playPauseButton;
Gtk::Button nextButton;
Gtk::ScrolledWindow imageWrapper;
void onSpotifyMprisUpdated(const MprisPlayer2Message &message);
};

View File

@@ -1,6 +1,7 @@
#include "services/dbus/mpris.hpp" #include "services/dbus/mpris.hpp"
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <sys/types.h>
#include "helpers/string.hpp" #include "helpers/string.hpp"
@@ -13,7 +14,6 @@ std::shared_ptr<MprisController> MprisController::getInstance() {
} }
MprisController::MprisController() { MprisController::MprisController() {
// 1. Connect to the Session Bus
Gio::DBus::Connection::get( Gio::DBus::Connection::get(
Gio::DBus::BusType::SESSION, Gio::DBus::BusType::SESSION,
sigc::mem_fun(*this, &MprisController::on_bus_connected)); sigc::mem_fun(*this, &MprisController::on_bus_connected));
@@ -44,7 +44,6 @@ void MprisController::on_bus_connected(const Glib::RefPtr<Gio::AsyncResult> &res
if (m_proxy) { if (m_proxy) {
std::cout << "Connected to: " << player_bus_name << std::endl; std::cout << "Connected to: " << player_bus_name << std::endl;
// uncomment if launch notification on start
signalNotification(); signalNotification();
m_proxy->signal_properties_changed().connect( m_proxy->signal_properties_changed().connect(
@@ -69,6 +68,11 @@ void MprisController::signalNotification() {
return; return;
} }
if (!metadata_var.is_of_type(Glib::VariantType("a{sv}"))) {
std::cout << "Unexpected metadata type." << std::endl;
return;
}
using MetadataMap = std::map<Glib::ustring, Glib::VariantBase>; using MetadataMap = std::map<Glib::ustring, Glib::VariantBase>;
MetadataMap metadata_map; MetadataMap metadata_map;
@@ -78,26 +82,46 @@ void MprisController::signalNotification() {
metadata_map = variant_dict.get(); metadata_map = variant_dict.get();
std::string title, artist, artwork_url; std::string title, artist, artwork_url;
if (metadata_map.count("xesam:title")) { if (metadata_map.count("xesam:title")) {
auto title_var = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(metadata_map["xesam:title"]); const auto &title_base = metadata_map["xesam:title"];
title = title_var.get(); if (title_base.is_of_type(Glib::VariantType("s"))) {
auto title_var = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(title_base);
title = title_var.get();
}
} }
if (metadata_map.count("xesam:artist")) { if (metadata_map.count("xesam:artist")) {
auto artist_var = metadata_map["xesam:artist"]; const auto &artist_var = metadata_map["xesam:artist"];
if (artist_var.is_of_type(Glib::VariantType("as"))) { if (artist_var.is_of_type(Glib::VariantType("as"))) {
auto artists = Glib::VariantBase::cast_dynamic<Glib::Variant<std::vector<Glib::ustring>>>(artist_var).get(); auto artists = Glib::VariantBase::cast_dynamic<Glib::Variant<std::vector<Glib::ustring>>>(artist_var).get();
if (!artists.empty()) { if (!artists.empty()) {
artist = artists[0]; // Take the first artist artist = artists[0]; // Take the first artist
} }
} else if (artist_var.is_of_type(Glib::VariantType("s"))) {
auto artist_str = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(artist_var);
artist = artist_str.get();
} }
} }
if (metadata_map.count("mpris:artUrl")) { if (metadata_map.count("mpris:artUrl")) {
auto art_var = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(metadata_map["mpris:artUrl"]); const auto &art_base = metadata_map["mpris:artUrl"];
artwork_url = art_var.get(); if (art_base.is_of_type(Glib::VariantType("s"))) {
auto art_var = Glib::VariantBase::cast_dynamic<Glib::Variant<Glib::ustring>>(art_base);
artwork_url = art_var.get();
}
}
int64_t length_us = 0;
if (metadata_map.count("mpris:length")) {
const auto &length_base = metadata_map["mpris:length"];
if (length_base.is_of_type(Glib::VariantType("x"))) {
auto length_var = Glib::VariantBase::cast_dynamic<Glib::Variant<gint64>>(length_base);
length_us = static_cast<int64_t>(length_var.get());
} else if (length_base.is_of_type(Glib::VariantType("t"))) {
auto length_var = Glib::VariantBase::cast_dynamic<Glib::Variant<guint64>>(length_base);
length_us = static_cast<int64_t>(length_var.get());
}
} }
MprisPlayer2Message mpris; MprisPlayer2Message mpris;
@@ -107,6 +131,7 @@ void MprisController::signalNotification() {
mpris.play_pause = [this]() { this->toggle_play(); }; mpris.play_pause = [this]() { this->toggle_play(); };
mpris.next = [this]() { this->next_song(); }; mpris.next = [this]() { this->next_song(); };
mpris.previous = [this]() { this->previous_song(); }; mpris.previous = [this]() { this->previous_song(); };
mpris.length_ms = length_us;
mprisUpdatedSignal.emit(mpris); mprisUpdatedSignal.emit(mpris);
} }
@@ -136,3 +161,18 @@ void MprisController::next_song() {
m_proxy->call("Next"); m_proxy->call("Next");
} }
} }
void MprisController::emit_seeked(int64_t position_us) {
if (!m_proxy) {
return;
}
try {
Glib::VariantContainerBase params = Glib::VariantContainerBase::create_tuple(
Glib::Variant<gint64>::create(position_us));
m_proxy->call("Seek", params);
} catch (const Glib::Error &ex) {
std::cerr << "Error seeking: " << ex.what() << std::endl;
}
}

View File

@@ -29,24 +29,24 @@ void HyprlandService::init() {
auto monitorPtr = std::make_shared<Monitor>(); auto monitorPtr = std::make_shared<Monitor>();
std::string monitorName = item["name"].get<std::string>(); std::string monitorName = item["name"].get<std::string>();
int monitorId = item["id"].get<int>(); int monitorId = item["id"].get<int>();
monitorPtr->id = monitorId; monitorPtr->id = monitorId;
monitorPtr->name = monitorName; monitorPtr->name = monitorName;
monitorPtr->activeWorkspaceId = item["activeWorkspace"]["id"].get<int>(); monitorPtr->activeWorkspaceId = item["activeWorkspace"]["id"].get<int>();
monitorPtr->focused = item["focused"].get<bool>(); monitorPtr->focused = item["focused"].get<bool>();
this->monitors[monitorPtr->name] = monitorPtr; this->monitors[monitorPtr->name] = monitorPtr;
for (int i = 1; i <= NUM_WORKSPACES; i++) { for (int i = 1; i <= NUM_WORKSPACES; i++) {
std::shared_ptr<WorkspaceData> state = std::make_shared<WorkspaceData>(); std::shared_ptr<WorkspaceData> state = std::make_shared<WorkspaceData>();
int workspaceId = i + (NUM_WORKSPACES * monitorId); int workspaceId = i + (NUM_WORKSPACES * monitorId);
state->id = workspaceId; state->id = workspaceId;
state->monitorName = monitorName; state->monitorName = monitorName;
auto view = std::make_shared<WorkspaceIndicator>(workspaceId, std::to_string(i), onClick); auto view = std::make_shared<WorkspaceIndicator>(workspaceId, std::to_string(i), onClick);
auto workSpace = std::make_shared<Workspace>(); auto workSpace = std::make_shared<Workspace>();
workSpace->state = state; workSpace->state = state;
workSpace->view = view; workSpace->view = view;
monitorPtr->monitorWorkspaces[workspaceId] = workSpace; monitorPtr->monitorWorkspaces[workspaceId] = workSpace;
this->workspaces[workspaceId] = workSpace; this->workspaces[workspaceId] = workSpace;
@@ -76,7 +76,7 @@ void HyprlandService::init() {
auto workspacePtr = workspaces[workspace["id"].get<int>()]; auto workspacePtr = workspaces[workspace["id"].get<int>()];
auto state = workspacePtr->state; auto state = workspacePtr->state;
state->id = workspace["id"].get<int>(); state->id = workspace["id"].get<int>();
state->monitorName = workspace["monitor"].get<std::string>(); state->monitorName = workspace["monitor"].get<std::string>();
refreshIndicator(workspacePtr); refreshIndicator(workspacePtr);
@@ -106,9 +106,9 @@ void HyprlandService::bindHyprlandSocket() {
} }
auto socket_conditions = static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_ERR); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange) auto socket_conditions = static_cast<GIOCondition>(G_IO_IN | G_IO_HUP | G_IO_ERR); // NOLINT(clang-analyzer-optin.core.EnumCastOutOfRange)
GSource *source = g_unix_fd_source_new(socketFd, socket_conditions); GSource *source = g_unix_fd_source_new(socketFd, socket_conditions);
auto onSocketEvent = [](gint fd, GIOCondition , gpointer user_data) -> gboolean { auto onSocketEvent = [](gint fd, GIOCondition, gpointer user_data) -> gboolean {
HyprlandService *self = static_cast<HyprlandService *>(user_data); HyprlandService *self = static_cast<HyprlandService *>(user_data);
auto messages = SocketHelper::parseSocketMessage(fd, ">>"); auto messages = SocketHelper::parseSocketMessage(fd, ">>");
@@ -119,7 +119,7 @@ void HyprlandService::bindHyprlandSocket() {
return G_SOURCE_CONTINUE; return G_SOURCE_CONTINUE;
}; };
g_source_set_callback(source, reinterpret_cast<GSourceFunc>(reinterpret_cast<void*>(+onSocketEvent)), this, nullptr); g_source_set_callback(source, reinterpret_cast<GSourceFunc>(reinterpret_cast<void *>(+onSocketEvent)), this, nullptr);
g_source_attach(source, g_main_context_default()); g_source_attach(source, g_main_context_default());
g_source_unref(source); g_source_unref(source);
} }
@@ -173,11 +173,34 @@ void HyprlandService::onMonitorRemoved(std::string monitorName) {
this->monitors.erase(monitorName); this->monitors.erase(monitorName);
} }
void HyprlandService::onMoveWindow(std::string windowData) {
auto parts = StringHelper::split(windowData, ',');
std::string addr = "0x" + parts[0];
int newWorkspaceId = std::stoi(parts[1]);
if (this->clients.find(addr) == this->clients.end()) {
std::cerr << "[Hyprland] onMoveWindow: Client not found: " << addr << std::endl;
return;
}
auto clientPtr = this->clients[addr];
int oldWorkspaceId = clientPtr->workspaceId;
auto oldWorkspacePtr = workspaces[oldWorkspaceId];
oldWorkspacePtr->state->clients.erase(addr);
refreshIndicator(oldWorkspacePtr);
clientPtr->workspaceId = newWorkspaceId;
auto newWorkspacePtr = workspaces[newWorkspaceId];
newWorkspacePtr->state->clients[addr] = clientPtr;
refreshIndicator(newWorkspacePtr);
}
// void HyprlandService::onMonitorAdded(std::string monitorName) { // void HyprlandService::onMonitorAdded(std::string monitorName) {
// // this->signalMonitorAdded.emit(); // // this->signalMonitorAdded.emit();
// } // }
void HyprlandService::onOpenWindow(std::string windowData) { void HyprlandService::onOpenWindow(std::string windowData) {
auto parts = StringHelper::split(windowData, ','); auto parts = StringHelper::split(windowData, ',');
std::string addr = "0x" + parts[0]; std::string addr = "0x" + parts[0];
@@ -250,6 +273,10 @@ void HyprlandService::handleSocketMessage(SocketHelper::SocketMessage message) {
this->onMonitorRemoved(eventData); this->onMonitorRemoved(eventData);
break; break;
} }
case MOVE_WINDOW: {
this->onMoveWindow(eventData);
break;
}
} }
} }
void HyprlandService::onUrgent(std::string windowAddress) { void HyprlandService::onUrgent(std::string windowAddress) {

View File

@@ -0,0 +1,20 @@
#include "widgets/controlCenter/controlCenter.hpp"
ControlCenter::ControlCenter(std::string icon, std::string name)
: Popover(icon, name) {
this->popover->add_css_class("control-center-popover");
this->container.set_orientation(Gtk::Orientation::VERTICAL);
this->container.set_spacing(0);
this->container.set_margin_top(0);
this->container.set_margin_bottom(0);
this->container.set_margin_start(0);
this->container.set_margin_end(0);
set_popover_child(this->container);
this->container.append(this->mediaControlWidget);
}

View File

@@ -1,28 +1,19 @@
#include "widgets/controlCenter.hpp" #include "widgets/controlCenter/mediaControl.hpp"
#include "services/textureCache.hpp" #include "services/textureCache.hpp"
ControlCenter::ControlCenter(std::string icon, std::string name) MediaControlWidget::MediaControlWidget()
: Popover(icon, name) { : Gtk::Box(Gtk::Orientation::VERTICAL) {
this->popover->add_css_class("control-center-popover");
this->container.set_orientation(Gtk::Orientation::VERTICAL);
this->container.set_spacing(0);
this->container.set_margin_top(0);
this->container.set_margin_bottom(0);
this->container.set_margin_start(0);
this->container.set_margin_end(0);
this->container.append(this->spotifyContainer);
set_popover_child(this->container); this->set_orientation(Gtk::Orientation::VERTICAL);
this->set_size_request(200, 240);
this->set_hexpand(false);
this->set_vexpand(false);
this->add_css_class("control-center-spotify-container");
this->spotifyContainer.set_orientation(Gtk::Orientation::VERTICAL); this->append(this->topContainer);
this->spotifyContainer.set_size_request(200, 240); this->append(this->seekBarContainer);
this->spotifyContainer.set_hexpand(false); // Important: Don't let the main box expand freely this->append(this->bottomContainer);
this->spotifyContainer.set_vexpand(false);
this->spotifyContainer.add_css_class("control-center-spotify-container");
this->spotifyContainer.append(this->topContainer);
this->spotifyContainer.append(this->seekBarContainer);
this->spotifyContainer.append(this->bottomContainer);
this->backgroundImage.set_content_fit(Gtk::ContentFit::COVER); this->backgroundImage.set_content_fit(Gtk::ContentFit::COVER);
this->backgroundImage.set_can_shrink(true); this->backgroundImage.set_can_shrink(true);
@@ -44,8 +35,7 @@ ControlCenter::ControlCenter(std::string icon, std::string name)
this->topContainer.add_overlay(this->infoContainer); this->topContainer.add_overlay(this->infoContainer);
this->artistLabel.set_halign(Gtk::Align::START); this->artistLabel.set_halign(Gtk::Align::START);
this->titleLabel.set_halign(Gtk::Align::START); this->titleLabel.set_halign(Gtk::Align::START);
this->seekBarContainer.set_orientation(Gtk::Orientation::HORIZONTAL); this->seekBarContainer.set_orientation(Gtk::Orientation::HORIZONTAL);
this->seekBarContainer.set_vexpand(false); this->seekBarContainer.set_vexpand(false);
@@ -68,6 +58,13 @@ ControlCenter::ControlCenter(std::string icon, std::string name)
this->seekBar.set_halign(Gtk::Align::CENTER); this->seekBar.set_halign(Gtk::Align::CENTER);
this->seekBar.add_css_class("control-center-seek-bar"); this->seekBar.add_css_class("control-center-seek-bar");
this->seekBar.signal_value_changed().connect([this]() {
double fraction = this->seekBar.get_value() / 100.0;
int64_t new_position_us =
static_cast<int64_t>(fraction * static_cast<double>(this->totalLengthUs));
this->mprisController->emit_seeked(new_position_us); // in ms
this->resetSeekTimer(new_position_us);
});
this->bottomContainer.set_orientation(Gtk::Orientation::HORIZONTAL); this->bottomContainer.set_orientation(Gtk::Orientation::HORIZONTAL);
this->bottomContainer.set_vexpand(false); this->bottomContainer.set_vexpand(false);
@@ -101,20 +98,70 @@ ControlCenter::ControlCenter(std::string icon, std::string name)
}); });
this->mprisController->signal_mpris_updated().connect( this->mprisController->signal_mpris_updated().connect(
sigc::mem_fun(*this, &ControlCenter::onSpotifyMprisUpdated) sigc::mem_fun(*this, &MediaControlWidget::onSpotifyMprisUpdated));
);
this->artistLabel.set_text("Artist Name"); this->artistLabel.set_text("Artist Name");
this->artistLabel.add_css_class("control-center-spotify-artist-label"); this->artistLabel.add_css_class("control-center-spotify-artist-label");
this->titleLabel.set_text("Song Title"); this->titleLabel.set_text("Song Title");
this->titleLabel.add_css_class("control-center-spotify-title-label"); this->titleLabel.add_css_class("control-center-spotify-title-label");
this->resetSeekTimer(0);
} }
void ControlCenter::onSpotifyMprisUpdated(const MprisPlayer2Message &message) { void MediaControlWidget::onSpotifyMprisUpdated(const MprisPlayer2Message &message) {
this->artistLabel.set_text(message.artist); this->artistLabel.set_text(message.artist);
this->titleLabel.set_text(message.title); this->titleLabel.set_text(message.title);
if (auto texture = TextureCacheService::getInstance()->getTexture(message.artwork_url)) { if (auto texture = TextureCacheService::getInstance()->getTexture(message.artwork_url)) {
this->backgroundImage.set_paintable(texture); this->backgroundImage.set_paintable(texture);
} }
this->setTotalLength(message.length_ms);
this->setCurrentPosition(0);
this->resetSeekTimer(0);
}
void MediaControlWidget::setCurrentPosition(int64_t position_us) {
this->currentPositionUs = position_us;
int64_t seconds = (position_us / 1000000) % 60;
int64_t minutes = (position_us / (1000000 * 60)) % 60;
this->currentTimeLabel.set_text(
std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds));
if (totalLengthUs > 0) {
double fraction = static_cast<double>(currentPositionUs) / static_cast<double>(totalLengthUs);
this->seekBar.set_value(fraction * 100);
}
}
void MediaControlWidget::setTotalLength(int64_t length_us) {
this->totalLengthUs = length_us;
int64_t seconds = (length_us / 1000000) % 60;
int64_t minutes = (length_us / (1000000 * 60)) % 60;
this->totalTimeLabel.set_text(
std::to_string(minutes) + ":" + (seconds < 10 ? "0" : "") + std::to_string(seconds));
}
void MediaControlWidget::resetSeekTimer(int64_t start_position_us) {
if (seekTimerConnection.connected()) {
seekTimerConnection.disconnect();
}
setCurrentPosition(start_position_us);
seekTimerConnection = Glib::signal_timeout().connect(
sigc::mem_fun(*this, &MediaControlWidget::onSeekTick),
1000);
}
bool MediaControlWidget::onSeekTick() {
if (totalLengthUs <= 0) {
return true;
}
int64_t nextPosition = currentPositionUs + 1000000;
if (nextPosition > totalLengthUs) {
nextPosition = totalLengthUs;
}
setCurrentPosition(nextPosition);
return true;
} }