mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-29 19:20:28 +00:00
Updated prototype to run queries in the background and update on the main
thread. Also, track filtering works properly.
This commit is contained in:
parent
442697e9ce
commit
4474b96f28
@ -69,6 +69,8 @@ int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE previousInstance, LPTSTR co
|
||||
mainWindow.MoveTo(200, 200);
|
||||
|
||||
LibraryPtr library = LibraryFactory::Libraries().at(0);
|
||||
library->SetMessageQueue(mainWindow.Queue());
|
||||
|
||||
MasterTransport transport;
|
||||
PlaybackService playback(mainWindow.Queue(), library, transport);
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include "MainController.h"
|
||||
#include <core/library/LocalLibraryConstants.h>
|
||||
#include <core/runtime/Message.h>
|
||||
#include <glue/query/SearchTrackListQuery.h>
|
||||
#include <glue/util/Playback.h>
|
||||
#include <glue/util/Duration.h>
|
||||
@ -41,18 +42,27 @@
|
||||
using namespace musik::win;
|
||||
using namespace musik::core;
|
||||
using namespace musik::core::audio;
|
||||
using namespace musik::core::runtime;
|
||||
using namespace musik::glue;
|
||||
using namespace musik::core::library;
|
||||
using namespace win32cpp;
|
||||
|
||||
using ColumnRef = ListView::ColumnRef;
|
||||
using CellRendererRef = ListView::CellRendererRef;
|
||||
using TextCellRenderer = ListView::TextCellRenderer<uistring>;
|
||||
using TextCellRendererRef = std::shared_ptr<TextCellRenderer>;
|
||||
|
||||
static ColumnRef sNumberColumn = ListView::Column::Create(_T("#"), 50);
|
||||
static ColumnRef sTitleColumn = ListView::Column::Create(_T("title"));
|
||||
static ColumnRef sDurationColumn = ListView::Column::Create(_T("duration"), 100, TextAlignment::TextAlignRight);
|
||||
static ColumnRef sTitleColumn = ListView::Column::Create(_T("title"), 250);
|
||||
static ColumnRef sDurationColumn = ListView::Column::Create(_T("time"), 50, TextAlignment::TextAlignRight);
|
||||
static ColumnRef sAlbumColumn = ListView::Column::Create(_T("album"));
|
||||
static ColumnRef sArtistColumn = ListView::Column::Create(_T("artist"));
|
||||
|
||||
class TrackListModel : public ListView::Model {
|
||||
#define EDIT_VIEW_HEIGHT 22
|
||||
#define UPDATE_SEARCH_MESSAGE 1001
|
||||
#define UPDATE_SEARCH_DEBOUNCE_MS 750
|
||||
|
||||
class MainController::TrackListModel : public ListView::Model, public sigslot::has_slots<> {
|
||||
public:
|
||||
static std::shared_ptr<TrackListModel> Create(PlaybackService& playback) {
|
||||
return std::shared_ptr<TrackListModel>(new TrackListModel(playback));
|
||||
@ -60,30 +70,74 @@ class TrackListModel : public ListView::Model {
|
||||
|
||||
TrackListModel(PlaybackService& playback)
|
||||
: playback(playback) {
|
||||
this->SetRowCount(playback.Count());
|
||||
playback.TrackChanged.connect(this, &TrackListModel::OnTrackChanged);
|
||||
|
||||
this->normal.reset(new TextCellRenderer());
|
||||
|
||||
this->bold.reset(new TextCellRenderer());
|
||||
FontRef font = Font::Create();
|
||||
font->SetBold(true);
|
||||
this->bold->SetFont(font);
|
||||
}
|
||||
|
||||
void SetTrackList(std::shared_ptr<TrackList> trackList) {
|
||||
this->trackList = trackList;
|
||||
this->SetRowCount(trackList ? trackList->Count() : 0);
|
||||
this->InvalidateData();
|
||||
}
|
||||
|
||||
virtual uistring CellValueToString(int rowIndex, ColumnRef column) {
|
||||
TrackPtr track = playback.GetTrackAtIndex(rowIndex);
|
||||
if (track) {
|
||||
if (column == sNumberColumn) {
|
||||
return u8to16(track->GetValue(constants::Track::TRACK_NUM));
|
||||
}
|
||||
else if (column == sTitleColumn) {
|
||||
return u8to16(track->GetValue(constants::Track::TITLE));
|
||||
}
|
||||
else if (column == sDurationColumn) {
|
||||
return u8to16(duration::Duration(track->GetValue(constants::Track::DURATION)));
|
||||
}
|
||||
else if (column == sArtistColumn) {
|
||||
return u8to16(track->GetValue(constants::Track::ARTIST));
|
||||
}
|
||||
}
|
||||
return _T("");
|
||||
}
|
||||
|
||||
virtual CellRendererRef CellRenderer(int rowIndex, ColumnRef column) {
|
||||
TrackPtr track = this->trackList->Get(rowIndex);
|
||||
|
||||
if (track) {
|
||||
TextCellRendererRef renderer = this->normal;
|
||||
|
||||
if (playing && playing->Id() == track->Id()) {
|
||||
renderer = this->bold;
|
||||
}
|
||||
|
||||
uistring value;
|
||||
|
||||
if (column == sNumberColumn) {
|
||||
value = u8to16(track->GetValue(constants::Track::TRACK_NUM));
|
||||
}
|
||||
else if (column == sTitleColumn) {
|
||||
value = u8to16(track->GetValue(constants::Track::TITLE));
|
||||
}
|
||||
else if (column == sDurationColumn) {
|
||||
value = u8to16(duration::Duration(track->GetValue(constants::Track::DURATION)));
|
||||
}
|
||||
else if (column == sAlbumColumn) {
|
||||
value = u8to16(track->GetValue(constants::Track::ALBUM));
|
||||
}
|
||||
else if (column == sArtistColumn) {
|
||||
value = u8to16(track->GetValue(constants::Track::ARTIST));
|
||||
}
|
||||
|
||||
if (value.size()) {
|
||||
renderer->Set(value, column->Alignment());
|
||||
return renderer;
|
||||
}
|
||||
}
|
||||
|
||||
return TextCellRendererRef();
|
||||
}
|
||||
|
||||
private:
|
||||
void OnTrackChanged(size_t index, TrackPtr track) {
|
||||
this->playing = track;
|
||||
this->InvalidateData();
|
||||
}
|
||||
|
||||
std::shared_ptr<TrackList> trackList;
|
||||
PlaybackService& playback;
|
||||
TextCellRendererRef normal;
|
||||
TextCellRendererRef bold;
|
||||
TrackPtr playing;
|
||||
};
|
||||
|
||||
MainController::MainController(
|
||||
@ -92,22 +146,26 @@ MainController::MainController(
|
||||
LibraryPtr library)
|
||||
: mainWindow(mainWindow)
|
||||
, playback(playback)
|
||||
, library(library) {
|
||||
std::shared_ptr<SearchTrackListQuery> query =
|
||||
std::shared_ptr<SearchTrackListQuery>(new SearchTrackListQuery(library, "o'o"));
|
||||
, library(library)
|
||||
, trackListDirty(true) {
|
||||
library->QueryCompleted.connect(this, &MainController::OnLibraryQueryCompleted);
|
||||
|
||||
library->Enqueue(query, ILibrary::QuerySynchronous);
|
||||
this->trackListQuery.reset(new SearchTrackListQuery(library, ""));
|
||||
library->Enqueue(this->trackListQuery);
|
||||
|
||||
playback.Play(*query->GetResult(), 0);
|
||||
this->editView = this->mainWindow.AddChild(new EditView());
|
||||
this->editView->Changed.connect(this, &MainController::OnSearchEditChanged);
|
||||
|
||||
this->categoryList = this->mainWindow.AddChild(new ListView());
|
||||
this->trackListView = this->mainWindow.AddChild(new ListView());
|
||||
this->trackListView->RowActivated.connect(this, &MainController::OnTrackListRowActivated);
|
||||
this->trackListView->AddColumn(sNumberColumn);
|
||||
this->trackListView->AddColumn(sTitleColumn);
|
||||
this->trackListView->AddColumn(sAlbumColumn);
|
||||
this->trackListView->AddColumn(sArtistColumn);
|
||||
this->trackListView->AddColumn(sDurationColumn);
|
||||
|
||||
this->trackList = this->mainWindow.AddChild(new ListView());
|
||||
this->trackList->AddColumn(sNumberColumn);
|
||||
this->trackList->AddColumn(sTitleColumn);
|
||||
this->trackList->AddColumn(sDurationColumn);
|
||||
this->trackList->AddColumn(sArtistColumn);
|
||||
this->trackList->SetModel(TrackListModel::Create(this->playback));
|
||||
this->trackListModel = TrackListModel::Create(this->playback);
|
||||
this->trackListView->SetModel(this->trackListModel);
|
||||
|
||||
this->Layout();
|
||||
|
||||
@ -121,17 +179,57 @@ void MainController::Layout() {
|
||||
auto size = this->mainWindow.ClientSize();
|
||||
if (size.height > 0) {
|
||||
static int padding = 8;
|
||||
int categoryCx = size.width / 3;
|
||||
int tracksCx = size.width - (categoryCx + padding);
|
||||
int tracksX = categoryCx + padding;
|
||||
|
||||
this->categoryList->MoveTo(0, 0);
|
||||
this->categoryList->Resize(categoryCx, size.height);
|
||||
this->trackList->MoveTo(tracksX, 0);
|
||||
this->trackList->Resize(tracksCx, size.height);
|
||||
int editCx = (size.width * 2) / 3;
|
||||
int editX = (size.width / 2) - (editCx / 2);
|
||||
|
||||
this->editView->MoveTo(editX, padding);
|
||||
this->editView->Resize(editCx, EDIT_VIEW_HEIGHT);
|
||||
|
||||
int listY = padding + EDIT_VIEW_HEIGHT + padding;
|
||||
int tracksCx = size.width;
|
||||
int listCy = size.height - listY;
|
||||
|
||||
this->trackListView->MoveTo(0, listY);
|
||||
this->trackListView->Resize(tracksCx, listCy);
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::OnMainWindowResized(Window* window, Size size) {
|
||||
this->Layout();
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::OnTrackListRowActivated(ListView* list, int index) {
|
||||
if (trackListDirty) {
|
||||
this->trackListDirty = false;
|
||||
if (this->trackList) {
|
||||
this->playback.Play((*this->trackList), index);
|
||||
}
|
||||
}
|
||||
else {
|
||||
playback.Play(index);
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::ProcessMessage(musik::core::runtime::IMessage &message) {
|
||||
if (message.Type() == UPDATE_SEARCH_MESSAGE) {
|
||||
std::string term = u16to8(this->editView->Caption());
|
||||
this->trackListQuery.reset(new SearchTrackListQuery(library, term));
|
||||
library->Enqueue(this->trackListQuery);
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::OnLibraryQueryCompleted(IQueryPtr query) {
|
||||
if (this->trackListQuery.get() == query.get()) {
|
||||
this->trackListDirty = true;
|
||||
this->trackList = ((TrackListQueryBase *)query.get())->GetResult();
|
||||
this->trackListModel->SetTrackList(this->trackList);
|
||||
}
|
||||
}
|
||||
|
||||
void MainController::OnSearchEditChanged(win32cpp::EditView* editView) {
|
||||
if (editView == this->editView) {
|
||||
IMessageQueue& queue = mainWindow.Queue();
|
||||
queue.Debounce(Message::Create(this, UPDATE_SEARCH_MESSAGE, 0, 0), UPDATE_SEARCH_DEBOUNCE_MS);
|
||||
}
|
||||
}
|
||||
|
@ -35,13 +35,21 @@
|
||||
#pragma once
|
||||
|
||||
#include <app/view/MainWindow.h>
|
||||
|
||||
#include <core/runtime/IMessageQueue.h>
|
||||
#include <core/audio/PlaybackService.h>
|
||||
#include <core/library/ILibrary.h>
|
||||
|
||||
#include <glue/query/TrackListQueryBase.h>
|
||||
|
||||
#include <win32cpp/EditView.hpp>
|
||||
#include <win32cpp/ListView.hpp>
|
||||
|
||||
namespace musik { namespace win {
|
||||
class MainController : public sigslot::has_slots<> {
|
||||
class MainController :
|
||||
public sigslot::has_slots<>,
|
||||
public musik::core::runtime::IMessageTarget
|
||||
{
|
||||
public:
|
||||
MainController(
|
||||
MainWindow& mainWindow,
|
||||
@ -50,15 +58,30 @@ namespace musik { namespace win {
|
||||
|
||||
virtual ~MainController();
|
||||
|
||||
virtual void ProcessMessage(musik::core::runtime::IMessage &message);
|
||||
|
||||
private:
|
||||
class TrackListModel;
|
||||
|
||||
void OnMainWindowResized(win32cpp::Window* window, win32cpp::Size size);
|
||||
void OnTrackListRowActivated(win32cpp::ListView* list, int index);
|
||||
void OnLibraryQueryCompleted(musik::core::IQueryPtr query);
|
||||
void OnSearchEditChanged(win32cpp::EditView* editView);
|
||||
|
||||
void Layout();
|
||||
|
||||
MainWindow& mainWindow;
|
||||
musik::core::audio::PlaybackService& playback;
|
||||
musik::core::LibraryPtr library;
|
||||
|
||||
win32cpp::ListView* categoryList;
|
||||
win32cpp::ListView* trackList;
|
||||
MainWindow& mainWindow;
|
||||
|
||||
std::shared_ptr<musik::glue::TrackListQueryBase> trackListQuery;
|
||||
std::shared_ptr<TrackListModel> trackListModel;
|
||||
std::shared_ptr<musik::core::TrackList> trackList;
|
||||
|
||||
win32cpp::ListView* trackListView;
|
||||
win32cpp::EditView* editView;
|
||||
|
||||
bool trackListDirty;
|
||||
};
|
||||
} }
|
@ -8,31 +8,31 @@
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
// * Neither the name of the author nor the names of other contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <win32cpp/Window.hpp>
|
||||
#include <win32cpp/Win32Exception.hpp>
|
||||
#include <win32cpp/Color.hpp>
|
||||
#include <win32cpp/Font.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
|
||||
@ -363,6 +364,9 @@ public: // constructors
|
||||
///\param value The value to render
|
||||
///\param alignment The alignment to use
|
||||
/*ctor*/ TextCellRenderer(const T& value, TextAlignment alignment = TextAlignLeft);
|
||||
/*ctor*/ TextCellRenderer(TextAlignment aligntment = TextAlignLeft);
|
||||
void SetFont(FontRef font);
|
||||
void Set(const T& value, TextAlignment aligntment = TextAlignLeft);
|
||||
|
||||
public: // methods
|
||||
void Render(const ListView& listView, RenderParams& renderParams);
|
||||
@ -370,14 +374,33 @@ public: // methods
|
||||
protected: // instance data
|
||||
uistring textValue;
|
||||
TextAlignment alignment;
|
||||
FontRef font;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
/*ctor*/ ListView::TextCellRenderer<T>::TextCellRenderer(const T& value, TextAlignment alignment)
|
||||
: alignment(alignment)
|
||||
{
|
||||
this->Set(value, alignment);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
/*ctor*/ ListView::TextCellRenderer<T>::TextCellRenderer(TextAlignment alignment)
|
||||
{
|
||||
this->alignment = alignment;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ListView::TextCellRenderer<T>::Set(const T& value, TextAlignment alignment)
|
||||
{
|
||||
typedef boost::basic_format<uichar> format;
|
||||
this->textValue = (format(_T("%1%")) % value).str();
|
||||
this->alignment = alignment;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void ListView::TextCellRenderer<T>::SetFont(FontRef font)
|
||||
{
|
||||
this->font = font;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -390,13 +413,18 @@ void ListView::TextCellRenderer<T>::Render(const ListView& listView, Rend
|
||||
::SecureZeroMemory(&drawTextParams, sizeof(DRAWTEXTPARAMS));
|
||||
drawTextParams.cbSize = sizeof(DRAWTEXTPARAMS);
|
||||
drawTextParams.iLeftMargin = 6;
|
||||
//
|
||||
|
||||
int bufferSize = (int) this->textValue.size() + 4; // DrawTextEx may add up to 4 additional characters
|
||||
std::unique_ptr<uichar> buffer(new uichar[bufferSize]);
|
||||
::wcsncpy_s(buffer.get(), bufferSize, this->textValue.c_str(), this->textValue.size());
|
||||
//
|
||||
|
||||
RECT drawRect = renderParams.rect;
|
||||
//
|
||||
HGDIOBJ old = nullptr;
|
||||
|
||||
if (font) {
|
||||
old = ::SelectObject(renderParams.hdc, font->GetHFONT());
|
||||
}
|
||||
|
||||
::DrawTextEx(
|
||||
renderParams.hdc,
|
||||
buffer.get(),
|
||||
@ -404,6 +432,10 @@ void ListView::TextCellRenderer<T>::Render(const ListView& listView, Rend
|
||||
&drawRect,
|
||||
this->alignment | DT_END_ELLIPSIS | DT_VCENTER | DT_SINGLELINE,
|
||||
&drawTextParams);
|
||||
|
||||
if (font) {
|
||||
::SelectObject(renderParams.hdc, old);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
Loading…
x
Reference in New Issue
Block a user