Added scrollbar support to ListOverlay.

This commit is contained in:
casey langen 2018-02-10 13:02:52 -08:00
parent cfdd4db53d
commit bcec9dc7ca
10 changed files with 226 additions and 36 deletions

View File

@ -42,6 +42,7 @@ set (CUBE_SRCS
./cursespp/Screen.cpp
./cursespp/ScrollableWindow.cpp
./cursespp/ScrollAdapterBase.cpp
./cursespp/Scrollbar.cpp
./cursespp/SimpleScrollAdapter.cpp
./cursespp/SingleLineEntry.cpp
./cursespp/Text.cpp

View File

@ -34,6 +34,7 @@
#include <stdafx.h>
#include "ListOverlay.h"
#include "Scrollbar.h"
#include "Colors.h"
#include "Screen.h"
#include "Text.h"
@ -43,6 +44,40 @@ using namespace cursespp;
#define VERTICAL_PADDING 2
#define DEFAULT_WIDTH 26
/* a little custom type that allows us to draw a scrollbar
ourself, outside of the list window frame. */
class ListOverlay::CustomListWindow : public ListWindow {
public:
using Callback = std::function<void()>;
CustomListWindow(Callback decorator, Callback adapterChanged)
: ListWindow() {
this->decorator = decorator;
this->adapterChanged = adapterChanged;
}
virtual void OnAdapterChanged() {
if (adapterChanged) { adapterChanged(); };
ListWindow::OnAdapterChanged();
}
void Reset() {
decorator = adapterChanged = Callback();
}
size_t EntryCount() {
return this->GetScrollAdapter().GetEntryCount();
}
protected:
virtual void DecorateFrame() {
if (decorator) { decorator(); }
}
private:
Callback decorator, adapterChanged;
};
ListOverlay::ListOverlay() {
this->SetFrameVisible(true);
this->SetFrameColor(CURSESPP_OVERLAY_FRAME);
@ -53,7 +88,20 @@ ListOverlay::ListOverlay() {
this->width = this->height = 0;
this->setWidth = this->setWidthPercent = 0;
this->listWindow.reset(new ListWindow());
auto decorator = [this] {
if (this->ScrollbarVisible()) {
Scrollbar::Draw(this->listWindow.get(), this->scrollbar.get());
}
};
auto adapterChanged = [this] { this->Layout(); };
this->scrollbar.reset(new Window());
this->scrollbar->SetFrameVisible(false);
this->scrollbar->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->AddWindow(this->scrollbar);
this->listWindow.reset(new CustomListWindow(decorator, adapterChanged));
this->listWindow->SetContentColor(CURSESPP_OVERLAY_CONTENT);
this->listWindow->SetFocusedContentColor(CURSESPP_OVERLAY_CONTENT);
this->listWindow->SetFrameVisible(false);
@ -62,6 +110,7 @@ ListOverlay::ListOverlay() {
}
ListOverlay::~ListOverlay() {
this->listWindow->Reset();
}
void ListOverlay::Layout() {
@ -74,21 +123,41 @@ void ListOverlay::Layout() {
this->width,
this->height);
this->listWindow->MoveAndResize(
1, /* one pixel padding L and R */
2, /* below the title, plus an extra space */
this->GetContentWidth() - 2,
this->height - 4); /* top and bottom padding + title */
bool scrollbar = this->ScrollbarVisible();
auto contentWidth = this->GetContentWidth() - 2; /* subtract padding */
auto contentHeight = this->height - 4; /* top and bottom padding + title */
auto startX = 1; /* L padding */
auto startY = 2; /* below the title, plus an extra space */
auto listWidth = scrollbar ? contentWidth - 2 : contentWidth;
this->listWindow->MoveAndResize(startX, startY, listWidth, contentHeight);
auto index = this->listWindow->GetSelectedIndex();
if (!this->listWindow->IsEntryVisible(index)) {
this->listWindow->ScrollTo(index);
}
if (scrollbar) {
this->scrollbar->Show();
this->scrollbar->MoveAndResize(contentWidth, startY, 1, contentHeight);
}
else {
this->scrollbar->Hide();
}
this->Redraw();
}
}
bool ListOverlay::ScrollbarVisible() {
#ifndef __FreeBSD__
auto contentHeight = this->height - 4; /* top and bottom padding + title */
return (int) this->listWindow->EntryCount() > contentHeight;
#else
return false;
#endif
}
ListOverlay& ListOverlay::SetTitle(const std::string& title) {
this->title = title;
this->RecalculateSize();

View File

@ -72,8 +72,11 @@ namespace cursespp {
virtual void OnVisibilityChanged(bool visible);
private:
class CustomListWindow;
void Redraw();
void RecalculateSize();
bool ScrollbarVisible();
std::string title;
int x, y;
@ -81,7 +84,8 @@ namespace cursespp {
int setWidth, setWidthPercent;
bool autoDismiss;
IScrollAdapterPtr adapter;
std::shared_ptr<ListWindow> listWindow;
std::shared_ptr<CustomListWindow> listWindow;
std::shared_ptr<Window> scrollbar;
ItemSelectedCallback itemSelectedCallback;
DeleteKeyCallback deleteKeyCallback;
};

View File

@ -35,6 +35,7 @@
#include <stdafx.h>
#include <algorithm>
#include "ListWindow.h"
#include "Scrollbar.h"
using namespace cursespp;
@ -71,35 +72,9 @@ void ListWindow::Invalidate() {
}
void ListWindow::DecorateFrame() {
#ifndef __FreeBSD__
if (this->showScrollbar) {
int height = this->GetHeight();
auto *adapter = &this->GetScrollAdapter();
if (adapter && this->IsFrameVisible() && height > 2) {
auto& pos = this->GetScrollPosition();
float range = (float)height - 2.0f;
float total = (float)std::max((size_t)1, adapter->GetEntryCount());
int offset;
if (range > total) {
offset = -1;
}
else {
float percent = (float)pos.logicalIndex / total;
offset = (int)(range * percent) + 1;
}
auto frame = this->GetFrame();
for (int i = 1; i < height - 1; i++) {
wmove(frame, i, this->GetWidth() - 1);
if (i == offset) wattron(frame, A_REVERSE);
waddch(frame, (i == offset) ? ' ' : ACS_VLINE);
if (i == offset) wattroff(frame, A_REVERSE);
}
}
if (this->IsFrameVisible()) {
Scrollbar::Draw(this);
}
#endif
}
void ListWindow::ScrollToTop() {

View File

@ -82,6 +82,8 @@ namespace cursespp {
virtual const IScrollAdapter::ScrollPosition& GetScrollPosition();
protected:
friend class Scrollbar;
virtual IScrollAdapter& GetScrollAdapter();
virtual IScrollAdapter::ScrollPosition& GetMutableScrollPosition();
virtual void OnRedraw();

View File

@ -0,0 +1,87 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 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 "Scrollbar.h"
using namespace cursespp;
void Scrollbar::Draw(ListWindow* list, Window* target) {
#ifndef __FreeBSD__
int height = list->GetHeight();
auto *adapter = &list->GetScrollAdapter();
if (adapter && height > 2) {
auto& pos = list->GetScrollPosition();
/* these are defaults for drawing to an external view, that
is, when target != list. */
int xOffset = 0;
int from = 0, to = height;
WINDOW* frame = nullptr;
float range = (float) height;
size_t minY = 0;
if (!target || target == list) {
/* if we're drawing on top of the ListWindow's frame,
we need to account for the padding it provides. */
frame = list->GetFrame();
xOffset = list->GetWidth() - 1;
++from; --to;
range -= 2.0f;
minY = 1;
}
else {
frame = target->GetFrame();
}
float total = (float) std::max(minY, adapter->GetEntryCount());
int yOffset;
if (range > total) {
yOffset = -1;
}
else {
float percent = (float)pos.logicalIndex / total;
yOffset = (int)(range * percent) + minY;
}
for (int i = from; i < to; i++) {
wmove(frame, i, xOffset);
if (i == yOffset) wattron(frame, A_REVERSE);
waddch(frame, (i == yOffset) ? ' ' : ACS_VLINE);
if (i == yOffset) wattroff(frame, A_REVERSE);
}
}
#endif
}

View File

@ -0,0 +1,44 @@
//////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2007-2017 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 "ListWindow.h"
namespace cursespp {
class Scrollbar {
public:
static void Draw(ListWindow* window, Window* target = nullptr);
};
}

View File

@ -239,7 +239,7 @@ void Window::MoveAndResize(int x, int y, int width, int height) {
absX != this->lastAbsoluteX ||
absY != this->lastAbsoluteY;
if (sizeChanged || positionChanged || badBounds) {
if (sizeChanged || positionChanged || this->badBounds || !this->content) {
this->lastAbsoluteX = absX;
this->lastAbsoluteY = absY;
this->width = width;

View File

@ -180,6 +180,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\dll\vorbis\*" "$(TargetDir)" /Y /e</
<ClCompile Include="cursespp\Screen.cpp" />
<ClCompile Include="cursespp\ScrollableWindow.cpp" />
<ClCompile Include="cursespp\ScrollAdapterBase.cpp" />
<ClCompile Include="cursespp\Scrollbar.cpp" />
<ClCompile Include="cursespp\ShortcutsWindow.cpp" />
<ClCompile Include="cursespp\SimpleScrollAdapter.cpp" />
<ClCompile Include="cursespp\SingleLineEntry.cpp" />
@ -253,6 +254,7 @@ xcopy "$(SolutionDir)src\3rdparty\bin\win32\dll\vorbis\*" "$(TargetDir)" /Y /e</
<ClInclude Include="cursespp\Screen.h" />
<ClInclude Include="cursespp\ScrollableWindow.h" />
<ClInclude Include="cursespp\ScrollAdapterBase.h" />
<ClInclude Include="cursespp\Scrollbar.h" />
<ClInclude Include="cursespp\ShortcutsWindow.h" />
<ClInclude Include="cursespp\SimpleScrollAdapter.h" />
<ClInclude Include="cursespp\SingleLineEntry.h" />

View File

@ -150,6 +150,9 @@
<ClCompile Include="app\overlay\BrowseOverlays.cpp">
<Filter>app\overlay</Filter>
</ClCompile>
<ClCompile Include="cursespp\Scrollbar.cpp">
<Filter>cursespp\util</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="stdafx.h" />
@ -352,6 +355,9 @@
<ClInclude Include="app\overlay\BrowseOverlays.h">
<Filter>app\overlay</Filter>
</ClInclude>
<ClInclude Include="cursespp\Scrollbar.h">
<Filter>cursespp\util</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="cursespp">