217 lines
8.0 KiB
C++
217 lines
8.0 KiB
C++
#include "widgets/controlCenter/mediaControl.hpp"
|
|
|
|
#include "helpers/string.hpp"
|
|
#include "services/textureCache.hpp"
|
|
|
|
MediaControlWidget::MediaControlWidget()
|
|
: Gtk::Box(Gtk::Orientation::VERTICAL) {
|
|
|
|
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->append(this->topContainer);
|
|
this->append(this->seekBarContainer);
|
|
this->append(this->bottomContainer);
|
|
|
|
this->backgroundImage.set_content_fit(Gtk::ContentFit::COVER);
|
|
this->backgroundImage.set_can_shrink(true);
|
|
|
|
this->imageWrapper.set_policy(Gtk::PolicyType::NEVER, Gtk::PolicyType::NEVER);
|
|
this->imageWrapper.set_child(this->backgroundImage);
|
|
|
|
this->topContainer.set_child(this->imageWrapper);
|
|
|
|
this->topContainer.set_size_request(200, 100);
|
|
this->topContainer.set_vexpand(false);
|
|
this->topContainer.set_hexpand(true);
|
|
|
|
this->infoContainer.set_orientation(Gtk::Orientation::VERTICAL);
|
|
this->infoContainer.set_valign(Gtk::Align::END);
|
|
this->infoContainer.set_halign(Gtk::Align::START);
|
|
this->infoContainer.append(this->artistLabel);
|
|
this->infoContainer.append(this->titleLabel);
|
|
this->topContainer.add_overlay(this->infoContainer);
|
|
|
|
this->artistLabel.set_halign(Gtk::Align::START);
|
|
this->titleLabel.set_halign(Gtk::Align::START);
|
|
|
|
this->seekBarContainer.set_orientation(Gtk::Orientation::HORIZONTAL);
|
|
this->seekBarContainer.set_vexpand(false);
|
|
this->seekBarContainer.set_hexpand(true);
|
|
this->seekBarContainer.set_halign(Gtk::Align::CENTER);
|
|
this->seekBarContainer.append(this->currentTimeLabel);
|
|
this->seekBarContainer.append(this->seekBar);
|
|
this->seekBarContainer.append(this->totalTimeLabel);
|
|
|
|
this->currentTimeLabel.set_text("0:00");
|
|
this->currentTimeLabel.set_halign(Gtk::Align::START);
|
|
this->totalTimeLabel.set_text("0:00");
|
|
this->totalTimeLabel.set_halign(Gtk::Align::END);
|
|
this->seekBar.set_range(0, 100);
|
|
this->seekBar.set_value(0);
|
|
this->seekBar.set_orientation(Gtk::Orientation::HORIZONTAL);
|
|
this->seekBar.set_draw_value(false);
|
|
this->seekBar.set_size_request(120, -1);
|
|
this->seekBar.set_hexpand(true);
|
|
this->seekBar.set_halign(Gtk::Align::CENTER);
|
|
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 - this->currentPositionUs); // in us
|
|
this->resetSeekTimer(new_position_us);
|
|
});
|
|
|
|
this->bottomContainer.set_orientation(Gtk::Orientation::HORIZONTAL);
|
|
this->bottomContainer.set_vexpand(false);
|
|
this->bottomContainer.set_hexpand(false);
|
|
this->bottomContainer.set_valign(Gtk::Align::START);
|
|
this->bottomContainer.set_homogeneous(true);
|
|
this->topContainer.set_vexpand(false);
|
|
this->topContainer.set_hexpand(true);
|
|
this->bottomContainer.append(this->previousButton);
|
|
this->bottomContainer.append(this->playPauseButton);
|
|
this->bottomContainer.append(this->nextButton);
|
|
|
|
this->previousButton.set_label("\u23EE"); // Previous track symbol
|
|
this->previousButton.add_css_class("notification-button");
|
|
this->previousButton.add_css_class("notification-icon-button");
|
|
this->playPauseButton.set_label("\u23EF"); // Play/Pause symbol
|
|
this->playPauseButton.add_css_class("notification-button");
|
|
this->playPauseButton.add_css_class("notification-icon-button");
|
|
this->nextButton.set_label("\u23ED"); // Next track symbol
|
|
this->nextButton.add_css_class("notification-button");
|
|
this->nextButton.add_css_class("notification-icon-button");
|
|
|
|
this->previousButton.signal_clicked().connect([this]() {
|
|
this->mprisController->previous_song();
|
|
});
|
|
this->playPauseButton.signal_clicked().connect([this]() {
|
|
this->mprisController->toggle_play();
|
|
});
|
|
this->nextButton.signal_clicked().connect([this]() {
|
|
this->mprisController->next_song();
|
|
});
|
|
|
|
this->mprisController->signal_mpris_updated().connect(
|
|
sigc::mem_fun(*this, &MediaControlWidget::onSpotifyMprisUpdated));
|
|
|
|
this->mprisController->signal_playback_status_changed().connect(
|
|
[this](MprisController::PlaybackStatus status) {
|
|
this->onRunningStateChanged(status);
|
|
});
|
|
|
|
this->mprisController->signal_playback_position_changed().connect(
|
|
[this](int64_t position_us) {
|
|
this->setCurrentPosition(position_us);
|
|
});
|
|
|
|
this->artistLabel.set_text("Artist Name");
|
|
this->artistLabel.add_css_class("control-center-spotify-artist-label");
|
|
this->titleLabel.set_text("Song Title");
|
|
this->titleLabel.add_css_class("control-center-spotify-title-label");
|
|
|
|
this->resetSeekTimer(0);
|
|
}
|
|
|
|
void MediaControlWidget::onSpotifyMprisUpdated(const MprisPlayer2Message &message) {
|
|
std::string artistText = "Unknown Artist";
|
|
if (!message.artist.empty()) {
|
|
artistText = StringHelper::trimToSize(message.artist[0], 30);
|
|
}
|
|
this->artistLabel.set_text(artistText);
|
|
this->titleLabel.set_text(message.title);
|
|
|
|
if (auto texture = TextureCacheService::getInstance()->getTexture(message.artwork_url)) {
|
|
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;
|
|
}
|
|
|
|
void MediaControlWidget::onRunningStateChanged(MprisController::PlaybackStatus status) {
|
|
switch (status) {
|
|
case MprisController::PlaybackStatus::Playing:
|
|
this->onPlay();
|
|
break;
|
|
case MprisController::PlaybackStatus::Paused:
|
|
this->onPause();
|
|
break;
|
|
case MprisController::PlaybackStatus::Stopped:
|
|
this->onStop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void MediaControlWidget::onPlay() {
|
|
this->playPauseButton.set_label("\u23F8"); // Pause symbol
|
|
// strart seek timer if not already running
|
|
this->resetSeekTimer(currentPositionUs);
|
|
}
|
|
|
|
void MediaControlWidget::onPause() {
|
|
this->playPauseButton.set_label("\u23EF"); // Play symbol
|
|
if (seekTimerConnection.connected()) {
|
|
seekTimerConnection.disconnect();
|
|
}
|
|
}
|
|
|
|
void MediaControlWidget::onStop() {
|
|
this->playPauseButton.set_label("\u23EF"); // Play symbol
|
|
if (seekTimerConnection.connected()) {
|
|
seekTimerConnection.disconnect();
|
|
}
|
|
this->setCurrentPosition(0);
|
|
} |