Incremental work to support dynamic visualization selection by the user.

Added a new ListOverlay type (not quite done yet).
This commit is contained in:
casey langen 2016-11-06 20:55:09 -08:00
parent 05b404c0f0
commit a85b4f49e7
29 changed files with 529 additions and 66 deletions

View File

@ -32,8 +32,6 @@
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include "pch.hpp"
#include "Visualizer.h"
#include <core/plugin/PluginFactory.h>
@ -46,6 +44,10 @@ static std::vector<std::shared_ptr<ISpectrumVisualizer> > spectrumVisualizers;
static std::vector<std::shared_ptr<IPcmVisualizer> > pcmVisualizers;
static std::atomic<bool> initialized;
static std::shared_ptr<IVisualizer> selectedVisualizer;
static ISpectrumVisualizer* spectrumVisualizer = nullptr;
static IPcmVisualizer* pcmVisualizer = nullptr;
namespace musik {
namespace core {
namespace audio {
@ -69,45 +71,37 @@ namespace musik {
}
ISpectrumVisualizer* SpectrumVisualizer() {
if (!initialized) {
init();
}
if (spectrumVisualizers.size()) {
return spectrumVisualizers[0].get();
}
return nullptr;
return spectrumVisualizer;
}
IPcmVisualizer* PcmVisualizer() {
return pcmVisualizer;
}
std::shared_ptr<IVisualizer> GetVisualizer(size_t index) {
if (index < pcmVisualizers.size()) {
return pcmVisualizers.at(index);
}
return spectrumVisualizers.at(index - pcmVisualizers.size());
}
size_t VisualizerCount() {
if (!initialized) {
init();
}
if (pcmVisualizers.size()) {
return pcmVisualizers[0].get();
}
return nullptr;
return pcmVisualizers.size() + spectrumVisualizers.size();
}
IVisualizer* SelectedVisualizer() {
IVisualizer* vis = SpectrumVisualizer();
return vis ? vis : PcmVisualizer();
void SetSelectedVisualizer(std::shared_ptr<IVisualizer> visualizer) {
selectedVisualizer = visualizer;
pcmVisualizer = dynamic_cast<IPcmVisualizer*>(visualizer.get());
spectrumVisualizer = dynamic_cast<ISpectrumVisualizer*>(visualizer.get());
}
void ToggleSelectedVisualizer() {
IVisualizer* vis = SelectedVisualizer();
if (vis) {
if (vis->Visible()) {
vis->Hide();
}
else {
vis->Show();
}
}
std::shared_ptr<IVisualizer> SelectedVisualizer() {
return selectedVisualizer;
}
}
}

View File

@ -42,7 +42,10 @@ namespace musik { namespace core { namespace audio { namespace vis {
ISpectrumVisualizer* SpectrumVisualizer();
IPcmVisualizer* PcmVisualizer();
IVisualizer* SelectedVisualizer();
void ToggleSelectedVisualizer();
std::shared_ptr<IVisualizer> GetVisualizer(size_t index);
size_t VisualizerCount();
void SetSelectedVisualizer(std::shared_ptr<IVisualizer> visualizer);
std::shared_ptr<IVisualizer> SelectedVisualizer();
} } } }

View File

@ -40,10 +40,10 @@
namespace musik { namespace core { namespace audio {
class IDecoderFactory{
public:
public:
virtual IDecoder* CreateDecoder() = 0;
virtual void Destroy() = 0;
virtual bool CanHandle(const char* type) const = 0;
};
} } }
} } }

View File

@ -34,9 +34,11 @@
#pragma once
#include "IPlugin.h"
namespace musik { namespace core { namespace audio {
class IVisualizer {
class IVisualizer : public IPlugin {
public:
virtual void Show() = 0;
virtual void Hide() = 0;

View File

@ -145,7 +145,7 @@ void DirectoryAdapter::SetDotfilesVisible(bool visible) {
}
}
IScrollAdapter::EntryPtr DirectoryAdapter::GetEntry(size_t index) {
IScrollAdapter::EntryPtr DirectoryAdapter::GetEntry(cursespp::ScrollableWindow* window, size_t index) {
if (dir.has_parent_path()) {
if (index == 0) {
return IScrollAdapter::EntryPtr(new SingleLineEntry(".."));

View File

@ -46,7 +46,7 @@ namespace musik {
virtual ~DirectoryAdapter();
virtual size_t GetEntryCount();
virtual EntryPtr GetEntry(size_t index);
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
void Select(size_t index);
std::string GetFullPathAt(size_t index);

View File

@ -0,0 +1,75 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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
// 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.
//
// 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.
//
//////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "VisualizerOverlay.h"
#include <core/audio/Visualizer.h>
#include <cursespp/App.h>
#include <cursespp/SimpleScrollAdapter.h>
#include <cursespp/ListOverlay.h>
using namespace musik::box;
using namespace musik::core::audio;
using namespace cursespp;
VisualizerOverlay::VisualizerOverlay() {
}
void VisualizerOverlay::Show() {
using Adapter = cursespp::SimpleScrollAdapter;
using ListOverlay = cursespp::ListOverlay;
std::shared_ptr<Adapter> adapter(new Adapter());
for (size_t i = 0; i < vis::VisualizerCount(); i++) {
adapter->AddEntry(vis::GetVisualizer(i)->Name());
}
adapter->SetSelectable(true);
std::shared_ptr<ListOverlay> dialog(new ListOverlay());
dialog->SetAdapter(adapter)
.SetTitle("visualizers")
.SetItemSelectedCallback(
[&](cursespp::IScrollAdapterPtr adapter, size_t index) {
vis::SetSelectedVisualizer(vis::GetVisualizer(index));
vis::SelectedVisualizer()->Show();
});
cursespp::App::Overlays().Push(dialog);
}

View File

@ -0,0 +1,47 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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
// 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.
//
// 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.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
namespace musik {
namespace box {
class VisualizerOverlay {
public:
static void Show();
private:
VisualizerOverlay();
};
}
}

View File

@ -35,7 +35,10 @@
#include "stdafx.h"
#include "GlobalHotkeys.h"
#include "Hotkeys.h"
#include <app/overlay/VisualizerOverlay.h>
#include <app/util/Playback.h>
#include <core/audio/Visualizer.h>
using musik::core::LibraryPtr;
@ -99,7 +102,13 @@ bool GlobalHotkeys::Handle(const std::string& kn) {
return true;
}
else if (Hotkeys::Is(Hotkeys::ToggleVisualizer, kn)) {
vis::ToggleSelectedVisualizer();
std::shared_ptr<IVisualizer> selected = vis::SelectedVisualizer();
if (selected && selected->Visible()) {
selected->Hide();
}
else {
VisualizerOverlay::Show();
}
return true;
}

View File

@ -225,7 +225,7 @@ size_t CategoryListView::Adapter::GetEntryCount() {
return parent.metadata ? parent.metadata->size() : 0;
}
IScrollAdapter::EntryPtr CategoryListView::Adapter::GetEntry(size_t index) {
IScrollAdapter::EntryPtr CategoryListView::Adapter::GetEntry(cursespp::ScrollableWindow* window, size_t index) {
std::string value = parent.metadata->at(index)->displayValue;
bool playing =

View File

@ -98,7 +98,7 @@ namespace musik {
Adapter(CategoryListView &parent);
virtual size_t GetEntryCount();
virtual EntryPtr GetEntry(size_t index);
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
private:
CategoryListView &parent;

View File

@ -244,7 +244,7 @@ static std::string formatWithoutAlbum(TrackPtr track, size_t width) {
% trackNum % title % duration % artist);
}
IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(size_t index) {
IScrollAdapter::EntryPtr TrackListView::Adapter::GetEntry(cursespp::ScrollableWindow* window, size_t index) {
bool selected = index == parent.GetSelectedIndex();
int64 attrs = selected ? COLOR_PAIR(CURSESPP_HIGHLIGHTED_LIST_ITEM) : -1LL;

View File

@ -86,7 +86,7 @@ namespace musik {
virtual ~Adapter() { }
virtual size_t GetEntryCount();
virtual EntryPtr GetEntry(size_t index);
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
private:
TrackListView &parent;

View File

@ -190,7 +190,7 @@ void DialogOverlay::Redraw() {
if (this->title.size()) {
wmove(c, currentY, currentX);
wattron(c, A_BOLD);
wprintw(c, text::Ellipsize(this->title, this->width - 2).c_str());
wprintw(c, text::Ellipsize(this->title, this->width - 4).c_str());
wattroff(c, A_BOLD);
currentY += 2;
}

View File

@ -72,7 +72,9 @@ namespace cursespp {
virtual void SetDisplaySize(size_t width, size_t height) = 0;
virtual size_t GetEntryCount() = 0;
virtual EntryPtr GetEntry(size_t index) = 0;
virtual EntryPtr GetEntry(ScrollableWindow* window, size_t index) = 0;
virtual void DrawPage(ScrollableWindow* window, size_t index, ScrollPosition *result = NULL) = 0;
};
typedef std::shared_ptr<IScrollAdapter> IScrollAdapterPtr;
}

View File

@ -0,0 +1,188 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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
// 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.
//
// 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.
//
//////////////////////////////////////////////////////////////////////////////
#include <stdafx.h>
#include "ListOverlay.h"
#include "Colors.h"
#include "Screen.h"
#include "Text.h"
using namespace cursespp;
#define HORIZONTAL_PADDING 4
#define VERTICAL_PADDING 2
#define DISMISS() \
OverlayStack* overlays = this->GetOverlayStack(); \
if (overlays) { \
overlays->Remove(this); \
}
ListOverlay::ListOverlay() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
this->SetContentColor(CURSESPP_OVERLAY_BACKGROUND);
this->width = this->height = 0;
this->listWindow.reset(new ListWindow());
this->listWindow->SetFrameVisible(false);
this->listWindow->SetFocusOrder(0);
this->AddWindow(this->listWindow);
}
ListOverlay::~ListOverlay() {
}
void ListOverlay::Layout() {
this->RecalculateSize();
if (this->width > 0 && this->height > 0) {
this->MoveAndResize(
HORIZONTAL_PADDING,
VERTICAL_PADDING,
this->width,
this->height);
int y = VERTICAL_PADDING + 3; /* below the border + title */
int cy = this->height - 4; /* top and bottom padding + title */
this->listWindow->MoveAndResize(
HORIZONTAL_PADDING + 1, y,
this->GetContentWidth(), cy);
}
}
ListOverlay& ListOverlay::SetTitle(const std::string& title) {
this->title = title;
this->RecalculateSize();
this->Layout();
this->Repaint();
return *this;
}
ListOverlay& ListOverlay::SetAdapter(IScrollAdapterPtr adapter) {
if (this->adapter != adapter) {
this->adapter = adapter;
this->listWindow->SetAdapter(adapter.get());
}
return *this;
}
ListOverlay& ListOverlay::SetItemSelectedCallback(ItemSelectedCallback cb) {
this->itemSelectedCallback = cb;
return *this;
}
bool ListOverlay::KeyPress(const std::string& key) {
if (key == "^[") { /* esc closes */
DISMISS();
return true;
}
else if (key == "KEY_ENTER") {
if (itemSelectedCallback) {
itemSelectedCallback(
this->adapter,
listWindow->GetSelectedIndex());
}
DISMISS();
return true;
}
return LayoutBase::KeyPress(key);
}
void ListOverlay::OnVisibilityChanged(bool visible) {
if (visible) {
this->SetFocus(this->listWindow);
this->Redraw();
}
}
void ListOverlay::RecalculateSize() {
this->width = 20;
this->height = 10;
//int lastWidth = this->width;
//this->width = std::max(0, Screen::GetWidth() - (HORIZONTAL_PADDING * 2));
//if (lastWidth != this->width) {
// /* 4 here: 2 for the frame padding (left/right), then two for the
// inner content padding so things aren't bunched up (left/right) */
// messageLines = text::BreakLines(this->message, this->width - 4);
//}
//this->height = 0; /* top padding */
//this->height += (this->title.size()) ? 2 : 0;
//this->height += (this->messageLines.size()) ? messageLines.size() + 1 : 0;
//this->height += 1; /* shortcuts */
///* ensure the overlay doesn't exceed the height of the screen,
//or things may get crashy. normally this will be done for us automatically
//in Window, but because we're free-floating we need to do it manually here. */
//int top = this->GetY();
//int bottom = top + this->height + VERTICAL_PADDING;
//int screenHeight = Screen::GetHeight();
//if (bottom > screenHeight) {
// this->height = screenHeight - top - VERTICAL_PADDING;
//}
//int left = this->GetX();
//int right = left + this->width + HORIZONTAL_PADDING;
//int screenWidth = Screen::GetWidth();
//if (right > screenWidth) {
// this->width = screenWidth - left - HORIZONTAL_PADDING;
//}
}
void ListOverlay::Redraw() {
if (this->width <= 0 || this->height <= 0) {
return;
}
WINDOW* c = this->GetContent();
const int currentX = 1;
int currentY = 0;
if (this->title.size()) {
wmove(c, currentY, currentX);
wattron(c, A_BOLD);
wprintw(c, text::Ellipsize(this->title, this->width - 4).c_str());
wattroff(c, A_BOLD);
currentY += 2;
}
}

View File

@ -0,0 +1,76 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2016 musikcube team
//
// All rights reserved.
//
// 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
// 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.
//
// 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.
//
//////////////////////////////////////////////////////////////////////////////
#pragma once
#include "OverlayBase.h"
#include "ListWindow.h"
#include <vector>
#include <map>
namespace cursespp {
class ListOverlay :
public OverlayBase
#if (__clang_major__ == 7 && __clang_minor__ == 3)
, public std::enable_shared_from_this<ListOverlay>
#endif
{
public:
using ItemSelectedCallback = std::function<void(IScrollAdapterPtr adapter, size_t index)>;
ListOverlay();
virtual ~ListOverlay();
ListOverlay& SetTitle(const std::string& title);
ListOverlay& SetAdapter(IScrollAdapterPtr adapter);
ListOverlay& SetItemSelectedCallback(ItemSelectedCallback cb);
virtual void Layout();
virtual bool KeyPress(const std::string& key);
protected:
virtual void OnVisibilityChanged(bool visible);
private:
void Redraw();
void RecalculateSize();
std::string title;
int width, height;
IScrollAdapterPtr adapter;
std::shared_ptr<ListWindow> listWindow;
ItemSelectedCallback itemSelectedCallback;
};
}

View File

@ -46,7 +46,7 @@ class EmptyAdapter : public IScrollAdapter {
public:
virtual void SetDisplaySize(size_t width, size_t height) { }
virtual size_t GetEntryCount() { return 0; }
virtual EntryPtr GetEntry(size_t index) { return IScrollAdapter::EntryPtr(); }
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index) { return IScrollAdapter::EntryPtr(); }
virtual void DrawPage(ScrollableWindow* window, size_t index, ScrollPosition *result = NULL) { }
};
@ -63,6 +63,13 @@ ListWindow::~ListWindow() {
}
void ListWindow::SetAdapter(IScrollAdapter* adapter) {
if (adapter != this->adapter) {
this->adapter = adapter;
this->ScrollToTop();
}
}
void ListWindow::ScrollToTop() {
this->SetSelectedIndex(0);
this->ScrollTo(0);

View File

@ -66,6 +66,8 @@ namespace cursespp {
virtual size_t GetSelectedIndex();
virtual void SetSelectedIndex(size_t index);
virtual void SetAdapter(IScrollAdapter* adapter);
virtual void OnAdapterChanged();
protected:

View File

@ -138,7 +138,7 @@ void MessageQueue::Post(IMessagePtr message, int64 delayMs) {
this->queue.insert(curr, m);
}
void MessageQueue::Debounce(IMessagePtr message, int delayMs) {
void MessageQueue::Debounce(IMessagePtr message, int64 delayMs) {
Remove(message->Target(), message->Type());
Post(message, delayMs);
}

View File

@ -47,7 +47,7 @@ namespace cursespp {
void Post(IMessagePtr message, int64 delayMs = 0);
void Remove(IMessageTarget *target, int type = -1);
void Debounce(IMessagePtr message, int delayMs = 0);
void Debounce(IMessagePtr message, int64 delayMs = 0);
void Dispatch();

View File

@ -36,11 +36,18 @@
#include "ScrollAdapterBase.h"
#include "ScrollableWindow.h"
#include "MultiLineEntry.h"
#include "ListWindow.h"
using namespace cursespp;
typedef IScrollAdapter::EntryPtr EntryPtr;
/* used for some calculations. it's ok to let this leak. if it's
a static instance var we will have non-deterministic behavior when
the instance is destructed, because the global message queue may
be torn down as well */
static ListWindow* DUMMY_SCROLLABLE_WINDOW = new ListWindow();
ScrollAdapterBase::ScrollAdapterBase() {
this->height = 0;
this->width = 0;
@ -72,7 +79,7 @@ void ScrollAdapterBase::GetVisibleItems(
/* forward search first... */
int end = (int) GetEntryCount();
for (int i = (int) desired; i < end && totalHeight > 0; i++) {
EntryPtr entry = this->GetEntry(i);
EntryPtr entry = this->GetEntry(DUMMY_SCROLLABLE_WINDOW, i);
entry->SetWidth(this->width);
totalHeight -= entry->GetLineCount();
target.push_back(entry);
@ -85,7 +92,7 @@ void ScrollAdapterBase::GetVisibleItems(
totalHeight = this->height;
int i = GetEntryCount() - 1;
while (i >= 0 && totalHeight >= 0) {
EntryPtr entry = this->GetEntry(i);
EntryPtr entry = this->GetEntry(DUMMY_SCROLLABLE_WINDOW, i);
entry->SetWidth(this->width);
int lines = entry->GetLineCount();

View File

@ -59,7 +59,7 @@ namespace cursespp {
ScrollPosition *result = nullptr);
virtual size_t GetEntryCount() = 0;
virtual EntryPtr GetEntry(size_t index) = 0;
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index) = 0;
virtual void SetItemDecorator(ItemDecorator decorator) { this->decorator = decorator; }

View File

@ -49,7 +49,7 @@ typedef IScrollAdapter::ScrollPosition ScrollPos;
#define REDRAW_VISIBLE_PAGE() \
{ \
ScrollPos& pos = GetScrollPosition(); \
ScrollPos& pos = GetMutableScrollPosition(); \
GetScrollAdapter().DrawPage( \
this, \
pos.firstVisibleEntryIndex, \
@ -68,14 +68,17 @@ void ScrollableWindow::OnDimensionsChanged() {
Window::OnDimensionsChanged();
IScrollAdapter& adapter = this->GetScrollAdapter();
ScrollPos& pos = this->GetScrollPosition();
adapter.SetDisplaySize(
this->GetContentWidth(),
this->GetContentHeight());
}
ScrollPos& ScrollableWindow::GetScrollPosition() {
ScrollPos& ScrollableWindow::GetMutableScrollPosition() {
return this->scrollPosition;
}
const ScrollPos& ScrollableWindow::GetScrollPosition() const {
return this->scrollPosition;
}
@ -91,13 +94,13 @@ bool ScrollableWindow::KeyPress(const std::string& key) {
if (key == "KEY_NPAGE") { this->PageDown(); return true; }
else if (key == "KEY_PPAGE") { this->PageUp(); return true; }
else if (key == "KEY_DOWN") {
else if (key == "KEY_DOWN") {
const size_t before = this->GetScrollPosition().logicalIndex;
this->ScrollDown();
const size_t after = this->GetScrollPosition().logicalIndex;
return !this->allowArrowKeyPropagation || (before != after);
}
else if (key == "KEY_UP") {
else if (key == "KEY_UP") {
const size_t before = this->GetScrollPosition().logicalIndex;
this->ScrollUp();
const size_t after = this->GetScrollPosition().logicalIndex;
@ -117,7 +120,7 @@ void ScrollableWindow::OnAdapterChanged() {
this->ScrollToBottom();
}
else {
ScrollPos &pos = this->GetScrollPosition();
ScrollPos &pos = this->GetMutableScrollPosition();
adapter->DrawPage(this, pos.firstVisibleEntryIndex, &pos);
this->Repaint();
}
@ -129,7 +132,7 @@ void ScrollableWindow::Show() {
}
void ScrollableWindow::ScrollToTop() {
GetScrollAdapter().DrawPage(this, 0, &this->GetScrollPosition());
GetScrollAdapter().DrawPage(this, 0, &this->GetMutableScrollPosition());
this->Repaint();
}
@ -137,13 +140,13 @@ void ScrollableWindow::ScrollToBottom() {
GetScrollAdapter().DrawPage(
this,
GetScrollAdapter().GetEntryCount(),
&this->GetScrollPosition());
&this->GetMutableScrollPosition());
this->Repaint();
}
void ScrollableWindow::ScrollUp(int delta) {
ScrollPos &pos = this->GetScrollPosition();
ScrollPos &pos = this->GetMutableScrollPosition();
if (pos.firstVisibleEntryIndex > 0) {
GetScrollAdapter().DrawPage(
@ -154,7 +157,7 @@ void ScrollableWindow::ScrollUp(int delta) {
}
void ScrollableWindow::ScrollDown(int delta) {
ScrollPos &pos = this->GetScrollPosition();
ScrollPos &pos = this->GetMutableScrollPosition();
GetScrollAdapter().DrawPage(
this, pos.firstVisibleEntryIndex + delta, &pos);
@ -169,7 +172,7 @@ size_t ScrollableWindow::GetPreviousPageEntryIndex() {
int i = this->GetScrollPosition().firstVisibleEntryIndex;
while (i >= 0) {
IScrollAdapter::EntryPtr entry = adapter->GetEntry(i);
IScrollAdapter::EntryPtr entry = adapter->GetEntry(this, i);
entry->SetWidth(width);
int count = entry->GetLineCount();
@ -195,7 +198,7 @@ void ScrollableWindow::PageDown() {
}
bool ScrollableWindow::IsLastItemVisible() {
ScrollPos &pos = this->GetScrollPosition();
ScrollPos &pos = this->GetMutableScrollPosition();
size_t lastIndex = pos.totalEntries;
lastIndex = (lastIndex == 0) ? lastIndex : lastIndex - 1;

View File

@ -65,9 +65,11 @@ namespace cursespp {
void SetAllowArrowKeyPropagation(bool allow = true);
const IScrollAdapter::ScrollPosition& GetScrollPosition() const;
protected:
virtual IScrollAdapter& GetScrollAdapter() = 0;
virtual IScrollAdapter::ScrollPosition& GetScrollPosition();
virtual IScrollAdapter::ScrollPosition& GetMutableScrollPosition();
size_t GetPreviousPageEntryIndex();
bool IsLastItemVisible();

View File

@ -37,6 +37,8 @@
#include "SimpleScrollAdapter.h"
#include "SingleLineEntry.h"
#include "MultiLineEntry.h"
#include "ScrollableWindow.h"
#include "Colors.h"
#include <boost/algorithm/string.hpp>
#include <utf8/utf8/unchecked.h>
@ -48,12 +50,17 @@ typedef IScrollAdapter::EntryPtr EntryPtr;
SimpleScrollAdapter::SimpleScrollAdapter() {
this->maxEntries = MAX_ENTRY_COUNT;
this->selectable = false;
}
SimpleScrollAdapter::~SimpleScrollAdapter() {
}
void SimpleScrollAdapter::SetSelectable(bool selectable) {
this->selectable = selectable;
}
void SimpleScrollAdapter::Clear() {
this->entries.clear();
}
@ -66,8 +73,19 @@ void SimpleScrollAdapter::SetMaxEntries(size_t maxEntries) {
this->maxEntries = maxEntries;
}
EntryPtr SimpleScrollAdapter::GetEntry(size_t index) {
return this->entries.at(index);
EntryPtr SimpleScrollAdapter::GetEntry(cursespp::ScrollableWindow* window, size_t index) {
auto entry = this->entries.at(index);
SingleLineEntry* styleable = static_cast<SingleLineEntry*>(entry.get());
styleable->SetAttrs(COLOR_PAIR(CURSESPP_TEXT_DEFAULT));
if (window && this->selectable) {
if (index == window->GetScrollPosition().logicalIndex) {
styleable->SetAttrs(COLOR_PAIR(CURSESPP_HIGHLIGHTED_LIST_ITEM));
}
}
return entry;
}
void SimpleScrollAdapter::AddEntry(std::shared_ptr<IEntry> entry) {
@ -78,3 +96,8 @@ void SimpleScrollAdapter::AddEntry(std::shared_ptr<IEntry> entry) {
entries.pop_front();
}
}
void SimpleScrollAdapter::AddEntry(const std::string& value) {
EntryPtr entry(new SingleLineEntry(value));
this->AddEntry(entry);
}

View File

@ -49,7 +49,10 @@ namespace cursespp {
virtual void Clear();
virtual size_t GetEntryCount();
virtual EntryPtr GetEntry(size_t index);
virtual EntryPtr GetEntry(cursespp::ScrollableWindow* window, size_t index);
void SetSelectable(bool selectable);
void AddEntry(const std::string& entry);
private:
typedef std::deque<EntryPtr> EntryList; /* TODO: this is O(n) lookup */
@ -57,5 +60,6 @@ namespace cursespp {
EntryList entries;
size_t maxEntries;
bool selectable;
};
}

View File

@ -127,6 +127,7 @@
<ClCompile Include="app\layout\TrackSearchLayout.cpp" />
<ClCompile Include="app\model\DirectoryAdapter.cpp" />
<ClCompile Include="app\model\TrackList.cpp" />
<ClCompile Include="app\overlay\VisualizerOverlay.cpp" />
<ClCompile Include="app\query\CategoryListViewQuery.cpp" />
<ClCompile Include="app\query\NowPlayingTrackListQuery.cpp" />
<ClCompile Include="app\query\CategoryTrackListQuery.cpp" />
@ -148,6 +149,7 @@
<ClCompile Include="cursespp\Colors.cpp" />
<ClCompile Include="cursespp\DialogOverlay.cpp" />
<ClCompile Include="cursespp\LayoutBase.cpp" />
<ClCompile Include="cursespp\ListOverlay.cpp" />
<ClCompile Include="cursespp\ListWindow.cpp" />
<ClCompile Include="cursespp\MultiLineEntry.cpp" />
<ClCompile Include="cursespp\OverlayStack.cpp" />
@ -181,6 +183,7 @@
<ClInclude Include="app\layout\TrackSearchLayout.h" />
<ClInclude Include="app\model\DirectoryAdapter.h" />
<ClInclude Include="app\model\TrackList.h" />
<ClInclude Include="app\overlay\VisualizerOverlay.h" />
<ClInclude Include="app\query\CategoryListViewQuery.h" />
<ClInclude Include="app\query\NowPlayingTrackListQuery.h" />
<ClInclude Include="app\query\SearchTrackListQuery.h" />
@ -217,6 +220,7 @@
<ClInclude Include="cursespp\IWindowGroup.h" />
<ClInclude Include="cursespp\IMessage.h" />
<ClInclude Include="cursespp\LayoutBase.h" />
<ClInclude Include="cursespp\ListOverlay.h" />
<ClInclude Include="cursespp\ListWindow.h" />
<ClInclude Include="cursespp\MultiLineEntry.h" />
<ClInclude Include="cursespp\OverlayBase.h" />

View File

@ -141,6 +141,12 @@
<ClCompile Include="cursespp\DialogOverlay.cpp">
<Filter>cursespp</Filter>
</ClCompile>
<ClCompile Include="cursespp\ListOverlay.cpp">
<Filter>cursespp</Filter>
</ClCompile>
<ClCompile Include="app\overlay\VisualizerOverlay.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -333,6 +339,12 @@
<ClInclude Include="app\util\Version.h">
<Filter>app\util</Filter>
</ClInclude>
<ClInclude Include="cursespp\ListOverlay.h">
<Filter>cursespp</Filter>
</ClInclude>
<ClInclude Include="app\overlay\VisualizerOverlay.h">
<Filter>app\overlay</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">
@ -359,5 +371,8 @@
<Filter Include="app\model">
<UniqueIdentifier>{b6720351-dd55-4b14-936a-09df56193c15}</UniqueIdentifier>
</Filter>
<Filter Include="app\overlay">
<UniqueIdentifier>{a84f242d-d70b-49e9-975e-63fc73954a2b}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>