Add a volume-slider replicant (VolumeView)

This commit is contained in:
Jaidyn Ann 2022-06-04 15:58:29 -05:00
parent 347a5fe6be
commit a6877e0385
10 changed files with 249 additions and 26 deletions

View File

@ -38,7 +38,8 @@ SRCS = src/App.cpp \
src/MediaPlayer.cpp \ src/MediaPlayer.cpp \
src/ReplicantView.cpp \ src/ReplicantView.cpp \
src/Song.cpp \ src/Song.cpp \
src/TextFile.cpp src/TextFile.cpp \
src/VolumeView.cpp
# Specify the resource definition files to use. Full or relative paths can be # Specify the resource definition files to use. Full or relative paths can be
# used. # used.

View File

@ -18,7 +18,7 @@ const uint32 COVER_MAKE_SQUARE = 'cvsq';
CoverView::CoverView(BRect frame) CoverView::CoverView(BRect frame)
: :
ReplicantView(frame, "Cover", B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE, B_FOLLOW_RIGHT) ReplicantView(frame, "Cover", B_FOLLOW_RIGHT, B_FOLLOW_ALL_SIDES, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE)
{ {
SetViewColor(B_TRANSPARENT_COLOR); SetViewColor(B_TRANSPARENT_COLOR);
fCover = NULL; fCover = NULL;

View File

@ -63,7 +63,7 @@ LyricsTextView::MouseDown(BPoint where)
LyricsView::LyricsView(BRect frame) LyricsView::LyricsView(BRect frame)
: :
ReplicantView(frame, "Lyrics", 0, B_FOLLOW_LEFT) ReplicantView(frame, "Lyrics", B_FOLLOW_LEFT)
{ {
BRect textRect(0, 0, Bounds().Width(), Bounds().Height() - 10); BRect textRect(0, 0, Bounds().Width(), Bounds().Height() - 10);
fTextView = new LyricsTextView(textRect, "lyricsText", textRect, fTextView = new LyricsTextView(textRect, "lyricsText", textRect,

View File

@ -6,10 +6,12 @@
#include "MainWindow.h" #include "MainWindow.h"
#include <LayoutBuilder.h> #include <LayoutBuilder.h>
#include <SplitView.h>
#include <TabView.h> #include <TabView.h>
#include "CoverView.h" #include "CoverView.h"
#include "LyricsView.h" #include "LyricsView.h"
#include "VolumeView.h"
MainWindow::MainWindow() MainWindow::MainWindow()
@ -26,9 +28,17 @@ MainWindow::MainWindow()
void void
MainWindow::_InitInterface() MainWindow::_InitInterface()
{ {
BView* controlsView = new BView("Controls", B_WILL_DRAW);
BLayoutBuilder::Group<>(controlsView, B_VERTICAL)
.Add(new VolumeView(BRect(BPoint(0,0), BSize(200.0, 20.0))))
.AddGlue()
.End();
BTabView* tabView = new BTabView("appletTabView"); BTabView* tabView = new BTabView("appletTabView");
tabView->AddTab(new LyricsView(BRect(BPoint(0,0), BSize(300.0, 200.0)))); tabView->AddTab(new LyricsView(BRect(BPoint(0,0), BSize(300.0, 200.0))));
tabView->AddTab(new CoverView(BRect(BPoint(0,0), BSize(300.0, 200.0)))); tabView->AddTab(new CoverView(BRect(BPoint(0,0), BSize(300.0, 200.0))));
tabView->AddTab(controlsView);
BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f) BLayoutBuilder::Group<>(this, B_VERTICAL, 0.0f)
.Add(tabView) .Add(tabView)

View File

@ -48,8 +48,8 @@ MediaPlayer::Instantiate(BMessage* data)
bool bool
MediaPlayer::IsPlaying() MediaPlayer::IsPlaying()
{ {
BMessage reply; BMessage send, reply;
_GetResponse("IsPlaying", &reply); _ScriptingCall("IsPlaying", &send, &reply);
return reply.GetBool("result", false); return reply.GetBool("result", false);
} }
@ -57,12 +57,30 @@ MediaPlayer::IsPlaying()
int64 int64
MediaPlayer::Position() MediaPlayer::Position()
{ {
BMessage reply; BMessage send, reply;
_GetResponse("Position", &reply); _ScriptingCall("Position", &send, &reply);
return reply.GetInt64("result", -1); return reply.GetInt64("result", -1);
} }
float
MediaPlayer::Volume()
{
BMessage send, reply;
_ScriptingCall("Volume", &send, &reply);
return reply.GetFloat("result", -1.0);
}
void
MediaPlayer::SetVolume(float volume)
{
BMessage send(B_SET_PROPERTY), reply;
send.AddFloat("data", volume);
_ScriptingCall("Volume", &send, &reply);
}
bool bool
MediaPlayer::CurrentItem(Song* song, bool duration) MediaPlayer::CurrentItem(Song* song, bool duration)
{ {
@ -90,8 +108,8 @@ MediaPlayer::SetWindow(int32 index)
int32 int32
MediaPlayer::CountWindows() MediaPlayer::CountWindows()
{ {
BMessage reply; BMessage send(B_COUNT_PROPERTIES), reply;
_GetResponse("Window", &reply, MP_NO_TRACK, B_COUNT_PROPERTIES); _ScriptingCall("Window", &send, &reply, MP_NO_TRACK);
int32 count; int32 count;
if (reply.FindInt32("result", &count) == B_OK) if (reply.FindInt32("result", &count) == B_OK)
@ -103,15 +121,15 @@ MediaPlayer::CountWindows()
bool bool
MediaPlayer::_GetSong(Song* song, int32 trackIndex, bool durationRequired) MediaPlayer::_GetSong(Song* song, int32 trackIndex, bool durationRequired)
{ {
BMessage reply; BMessage send, reply;
_GetResponse("URI", &reply, trackIndex); _ScriptingCall("URI", &send, &reply, trackIndex);
BString uriString; BString uriString;
if (reply.FindString("result", &uriString) != B_OK) if (reply.FindString("result", &uriString) != B_OK)
return false; return false;
int64 duration = -1; int64 duration = -1;
if (durationRequired) { if (durationRequired) {
_GetResponse("Duration", &reply, trackIndex); _ScriptingCall("Duration", &send, &reply, trackIndex);
if (reply.FindInt64("result", &duration) != B_OK) if (reply.FindInt64("result", &duration) != B_OK)
return false; return false;
} }
@ -122,17 +140,20 @@ MediaPlayer::_GetSong(Song* song, int32 trackIndex, bool durationRequired)
void void
MediaPlayer::_GetResponse(const char* attribute, BMessage* reply, int32 trackIndex, int32 what) MediaPlayer::_ScriptingCall(const char* attribute, BMessage* send, BMessage* reply, int32 trackIndex)
{ {
BMessage message; if (send->what == 0)
message.what = what; send->what = B_GET_PROPERTY;
message.AddSpecifier(attribute);
send->AddSpecifier(attribute);
if (trackIndex > 0) if (trackIndex > 0)
message.AddSpecifier("PlaylistTrack", trackIndex); send->AddSpecifier("PlaylistTrack", trackIndex);
else if (trackIndex == MP_CURRENT_TRACK) else if (trackIndex == MP_CURRENT_TRACK)
message.AddSpecifier("CurrentTrack"); send->AddSpecifier("CurrentTrack");
message.AddSpecifier("Window", fWindowIndex);
BMessenger("application/x-vnd.Haiku-MediaPlayer").SendMessage(&message, reply); send->AddSpecifier("Window", fWindowIndex);
BMessenger("application/x-vnd.Haiku-MediaPlayer").SendMessage(send, reply);
} }

View File

@ -30,6 +30,9 @@ public:
bool IsPlaying(); bool IsPlaying();
int64 Position(); int64 Position();
float Volume();
void SetVolume(float volume);
bool CurrentItem(Song* song, bool duration = true); bool CurrentItem(Song* song, bool duration = true);
bool PlaylistItem(Song* song, int32 index, bool duration = true); bool PlaylistItem(Song* song, int32 index, bool duration = true);
@ -38,8 +41,8 @@ public:
private: private:
bool _GetSong(Song* song, int32 trackIndex, bool durationRequired = true); bool _GetSong(Song* song, int32 trackIndex, bool durationRequired = true);
void _GetResponse(const char* attribute, BMessage* reply, void _ScriptingCall(const char* attribute, BMessage* send, BMessage* reply,
int32 trackIndex = MP_NO_TRACK, int32 what = B_GET_PROPERTY); int32 trackIndex = MP_NO_TRACK);
const char* _UriToPath(BString URI); const char* _UriToPath(BString URI);
int32 fWindowIndex; int32 fWindowIndex;

View File

@ -13,9 +13,11 @@
#include "MediaPlayer.h" #include "MediaPlayer.h"
ReplicantView::ReplicantView(BRect frame, const char* name, uint32 flags, uint32 draggerPlacement) ReplicantView::ReplicantView(BRect frame, const char* name, uint32 draggerPlacement, uint32 resize,
uint32 flags)
: :
BView(frame, name, B_FOLLOW_ALL_SIDES, B_TRANSPARENT_BACKGROUND | B_PULSE_NEEDED | flags) BView(frame, name, resize,
B_TRANSPARENT_BACKGROUND | B_DRAW_ON_CHILDREN | B_PULSE_NEEDED | flags)
{ {
BRect dragRect(frame.Width() - 10, 0, frame.Width(), frame.Height()); BRect dragRect(frame.Width() - 10, 0, frame.Width(), frame.Height());
uint32 dragFollow = B_FOLLOW_RIGHT; uint32 dragFollow = B_FOLLOW_RIGHT;

View File

@ -32,8 +32,10 @@ enum {
- MediaPlayer object */ - MediaPlayer object */
class ReplicantView : public BView { class ReplicantView : public BView {
public: public:
ReplicantView(BRect frame, const char* name, uint32 flags, ReplicantView(BRect frame, const char* name,
uint32 draggerPlacement); uint32 draggerPlacement = B_FOLLOW_RIGHT,
uint32 resize = B_FOLLOW_ALL_SIDES,
uint32 flags = 0);
ReplicantView(BMessage* archive); ReplicantView(BMessage* archive);
virtual status_t Archive(BMessage* data, bool deep = true) const; virtual status_t Archive(BMessage* data, bool deep = true) const;

148
src/VolumeView.cpp Normal file
View File

@ -0,0 +1,148 @@
/*
* Copyright 2022, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "VolumeView.h"
#include <cmath>
#include <Slider.h>
#include "MediaPlayer.h"
// The same color MediaPlayer uses, interface/VolumeSlider
static const rgb_color kVolumeGreen = (rgb_color){ 116, 224, 0, 255 };
VolumeView::VolumeView(BRect frame)
:
ReplicantView(frame, "Volume", B_FOLLOW_RIGHT, B_FOLLOW_LEFT_RIGHT, B_WILL_DRAW)
{
SetViewColor(B_TRANSPARENT_COLOR);
fSlider = new BSlider(frame, "volumeSlider", NULL, NULL, 0, 1000, B_HORIZONTAL, B_BLOCK_THUMB,
B_FOLLOW_LEFT_RIGHT);
fSlider->UseFillColor(true, &kVolumeGreen);
fSlider->SetViewColor(B_TRANSPARENT_COLOR);
AddChild(fSlider);
_Init();
}
VolumeView::VolumeView(BMessage* data)
:
ReplicantView(data)
{
fSlider = dynamic_cast<BSlider*>(FindView("volumeSlider"));
_Init();
}
status_t
VolumeView::Archive(BMessage* data, bool deep) const
{
status_t status = ReplicantView::Archive(data, deep);
data->AddString("class", "VolumeView");
data->AddString("add_on", APP_SIGNATURE);
return status;
}
VolumeView*
VolumeView::Instantiate(BMessage* data)
{
if (!validate_instantiation(data, "VolumeView"))
return NULL;
return new VolumeView(data);
}
void
VolumeView::MessageReceived(BMessage* msg)
{
if (msg->what != B_MOUSE_WHEEL_CHANGED || !fSlider->IsEnabled()) {
ReplicantView::MessageReceived(msg);
return;
}
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);
}
}
void
VolumeView::Pulse()
{
if (fSlider->Position() != fLastPos)
fMediaPlayer->SetVolume(_PositionToVolume(fSlider->Position()));
float volume = fMediaPlayer->Volume();
if (volume > 0) {
SetInactive(false);
fSlider->SetPosition(_VolumeToPosition(volume));
} else
SetInactive(true);
fLastPos = fSlider->Position();
}
void
VolumeView::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
VolumeView::_Init()
{
float height = 20.0;
fSlider->GetPreferredSize(NULL, &height);
SetExplicitMaxSize(BSize(B_SIZE_UNSET, height + 16));
fLastPos = 0;
Pulse();
}
// The VolumeSlider for MediaPlayer isn't quite linear… I've tried my best to replicate it
// without actually tearing the guts from MediaPlayer or expending too much effort.
// It's close enough, probably…
float
VolumeView::_VolumeToPosition(float volume)
{
if (volume > 1)
return volume / 2;
else if (volume != 0)
return (-.91 * pow(volume, 2)) + (1.3 * volume) + .06;
return 0;
}
// … please don't hate me :w:"
float
VolumeView::_PositionToVolume(float position)
{
if (position > .5)
return position * 2;
else if (position != 0) {
float vol = -130 + pow(-36400 * position + 19084, .5);
return abs(vol / 182);
}
return 0;
}

36
src/VolumeView.h Normal file
View File

@ -0,0 +1,36 @@
/*
* Copyright 2022, Jaidyn Levesque <jadedctrl@teknik.io>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#ifndef VOLUMEVIEW_H
#define VOLUMEVIEW_H
#include "ReplicantView.h"
class BSlider;
class VolumeView : public ReplicantView {
public:
VolumeView(BRect frame);
VolumeView(BMessage* archive);
virtual status_t Archive(BMessage* data, bool deep = true) const;
static VolumeView* Instantiate(BMessage* data);
virtual void MessageReceived(BMessage* msg);
virtual void Pulse();
virtual void SetInactive(bool inactive);
private:
void _Init();
float _VolumeToPosition(float volume);
float _PositionToVolume(float position);
BSlider* fSlider;
float fLastPos;
};
#endif // VOLUMEVIEW_H