From 44eda8ab5462097c7e6dea0856bb1d6e31bca005 Mon Sep 17 00:00:00 2001 From: Jaidyn Ann Date: Tue, 7 Jun 2022 06:23:02 -0500 Subject: [PATCH] Add a position-seeker replicant (PositionView) --- Makefile | 1 + src/MainWindow.cpp | 2 + src/MediaPlayer.cpp | 18 +++++ src/MediaPlayer.h | 2 + src/PositionView.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++ src/PositionView.h | 47 ++++++++++++ 6 files changed, 249 insertions(+) create mode 100644 src/PositionView.cpp create mode 100644 src/PositionView.h diff --git a/Makefile b/Makefile index f356bff..dcfd94e 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,7 @@ SRCS = src/App.cpp \ src/LyricsView.cpp \ src/MainWindow.cpp \ src/MediaPlayer.cpp \ + src/PositionView.cpp \ src/ReplicantView.cpp \ src/Song.cpp \ src/Util.cpp \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 378a6a0..82c72d6 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -11,6 +11,7 @@ #include "CoverView.h" #include "LyricsView.h" +#include "PositionView.h" #include "VolumeView.h" @@ -32,6 +33,7 @@ MainWindow::_InitInterface() BView* controlsView = new BView("Controls", B_WILL_DRAW); BLayoutBuilder::Group<>(controlsView, B_VERTICAL) .Add(new VolumeView(BRect(BPoint(0,0), BSize(100.0, 20.0)))) + .Add(new PositionView(BRect(BPoint(0,0), BSize(100.0, 20.0)))) .AddGlue() .End(); diff --git a/src/MediaPlayer.cpp b/src/MediaPlayer.cpp index a597d1b..0513bae 100644 --- a/src/MediaPlayer.cpp +++ b/src/MediaPlayer.cpp @@ -54,6 +54,15 @@ MediaPlayer::IsPlaying() } +int64 +MediaPlayer::Duration() +{ + BMessage send, reply; + _ScriptingCall("Duration", &send, &reply, MP_CURRENT_TRACK); + return reply.GetInt64("result", -1); +} + + int64 MediaPlayer::Position() { @@ -63,6 +72,15 @@ MediaPlayer::Position() } +void +MediaPlayer::SetPosition(int64 position) +{ + BMessage send(B_SET_PROPERTY), reply; + send.AddInt64("data", position); + _ScriptingCall("Position", &send, &reply); +} + + float MediaPlayer::Volume() { diff --git a/src/MediaPlayer.h b/src/MediaPlayer.h index 660b8b1..d524628 100644 --- a/src/MediaPlayer.h +++ b/src/MediaPlayer.h @@ -29,7 +29,9 @@ public: bool IsPlaying(); + int64 Duration(); int64 Position(); + void SetPosition(int64 position); float Volume(); void SetVolume(float volume); diff --git a/src/PositionView.cpp b/src/PositionView.cpp new file mode 100644 index 0000000..08532af --- /dev/null +++ b/src/PositionView.cpp @@ -0,0 +1,179 @@ +/* + * Copyright 2022, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ + +#include "PositionView.h" + +#include + +#include "MediaPlayer.h" + + +const char* kPositionDesc = "PositionView is a replicant that shows the position of currently playing item."; + +// The same color MediaPlayer uses, interface/SeekSlider +static const rgb_color kThumbRed = (rgb_color){ 255, 52, 52, 255 }; + +static const uint32 POSITION_CHANGED = 'vvch'; + + +SeekSlider::SeekSlider(BRect frame, const char* name, BMessage* message, int32 minValue, + int32 maxValue, orientation posture, uint32 resizingMode, uint32 flags) + : + BSlider(frame, name, NULL, message, minValue, maxValue, posture, B_TRIANGLE_THUMB, resizingMode, + flags) +{ + rgb_color fillColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + B_DARKEN_3_TINT); + UseFillColor(true, &fillColor); +} + + +void +SeekSlider::DrawThumb() +{ + if (!IsEnabled()) + return; + + BRect frame = ThumbFrame(); + be_control_look->DrawSliderTriangle(this, frame, frame, kThumbRed, 0, + B_HORIZONTAL); +} + + +PositionView::PositionView(BRect frame) + : + ReplicantView(frame, "Position", B_FOLLOW_RIGHT, B_FOLLOW_LEFT_RIGHT, + B_WILL_DRAW | B_DRAW_ON_CHILDREN | B_NAVIGABLE | B_FRAME_EVENTS) +{ + fDescription.SetTo(kPositionDesc); + + _InitInterface(); + Pulse(); +} + + +PositionView::PositionView(BMessage* data) + : + ReplicantView(data) +{ + fDescription.SetTo(kPositionDesc); + + // For some reason, the BSlider gets archived with a wacko frame― better to just nuke it. + fSlider = dynamic_cast(FindView("seekSlider")); + delete fSlider; + + _InitInterface(); + Pulse(); +} + + +status_t +PositionView::Archive(BMessage* data, bool deep) const +{ + status_t status = ReplicantView::Archive(data, deep); + + data->AddString("class", "PositionView"); + data->AddString("add_on", APP_SIGNATURE); + return status; +} + + +PositionView* +PositionView::Instantiate(BMessage* data) +{ + if (!validate_instantiation(data, "PositionView")) + return NULL; + return new PositionView(data); +} + + +void +PositionView::AttachedToWindow() +{ + fSlider->SetTarget(this); +} + + +void +PositionView::MessageReceived(BMessage* msg) +{ + switch (msg->what) { + case POSITION_CHANGED: + fMediaPlayer->SetPosition(_SliderToPosition(fSlider->Position(), + fMediaPlayer->Duration())); + break; + case B_MOUSE_WHEEL_CHANGED: + { + float scroll = 0.0f; + if ((msg->FindFloat("be:wheel_delta_x", &scroll) == B_OK && scroll != 0.0f) + || (msg->FindFloat("be:wheel_delta_y", &scroll) == B_OK && scroll != 0.0f)) + fSlider->SetPosition(fSlider->Position() + scroll * -0.03); + fMediaPlayer->SetPosition(_SliderToPosition(fSlider->Position(), + fMediaPlayer->Duration())); + break; + } + default: + ReplicantView::MessageReceived(msg); + } +} + + +void +PositionView::Pulse() +{ + int64 position = fMediaPlayer->Position(); + int64 duration = fMediaPlayer->Duration(); + if (duration > 0) { + SetInactive(false); + fSlider->SetPosition(_PositionToSlider(position, duration)); + } else + SetInactive(true); +} + + +void +PositionView::SetInactive(bool inactive) +{ + fSlider->SetEnabled(!inactive); + + if (inactive && fTransparentInactivity && !fSlider->IsHidden()) + fSlider->Hide(); + else if ((!inactive || !fTransparentInactivity) && fSlider->IsHidden()) + fSlider->Show(); + + ReplicantView::SetInactive(inactive); +} + + +void +PositionView::_InitInterface() +{ + SetViewColor(B_TRANSPARENT_COLOR); + + fSlider = new SeekSlider(BRect(), "seekSlider", NULL, 0, 1000, B_HORIZONTAL, + B_FOLLOW_LEFT_RIGHT); + fSlider->SetModificationMessage(new BMessage(POSITION_CHANGED)); + fSlider->SetViewColor(B_TRANSPARENT_COLOR); + AddChild(fSlider); + + float height = 20.0; + fSlider->GetPreferredSize(NULL, &height); + SetExplicitMaxSize(BSize(B_SIZE_UNSET, height + 16)); + fSlider->ResizeTo(Frame().Size()); +} + + +int64 +PositionView::_SliderToPosition(float sliderPos, int64 duration) +{ + return duration * sliderPos; +} + + +float +PositionView::_PositionToSlider(int64 position, int64 duration) +{ + return (double)position / (double)duration; +} diff --git a/src/PositionView.h b/src/PositionView.h new file mode 100644 index 0000000..bcc1ee4 --- /dev/null +++ b/src/PositionView.h @@ -0,0 +1,47 @@ +/* + * Copyright 2022, Jaidyn Levesque + * All rights reserved. Distributed under the terms of the MIT license. + */ +#ifndef POSITIONVIEW_H +#define POSITIONVIEW_H + +#include + +#include "ReplicantView.h" + + +class SeekSlider : public BSlider { +public: + SeekSlider(BRect frame, const char* name, BMessage* message, int32 minValue, + int32 maxValue, orientation posture, + uint32 resizingMode = B_FOLLOW_LEFT_TOP, + uint32 flags = B_NAVIGABLE | B_WILL_DRAW | B_FRAME_EVENTS); + + virtual void DrawThumb(); +}; + + +class PositionView : public ReplicantView { +public: + PositionView(BRect frame); + PositionView(BMessage* archive); + + virtual status_t Archive(BMessage* data, bool deep = true) const; + static PositionView* Instantiate(BMessage* data); + + void AttachedToWindow(); + + virtual void MessageReceived(BMessage* msg); + virtual void Pulse(); + + virtual void SetInactive(bool inactive); + +private: + void _InitInterface(); + int64 _SliderToPosition(float volume, int64 duration); + float _PositionToSlider(int64 position, int64 duration); + + SeekSlider* fSlider; +}; + +#endif // POSITIONVIEW_H