Add a volume-slider replicant (VolumeView)
This commit is contained in:
parent
347a5fe6be
commit
a6877e0385
3
Makefile
3
Makefile
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
Ŝarĝante…
Reference in New Issue