mirror of
https://github.com/mudita/MuditaOS.git
synced 2026-01-02 10:58:45 -05:00
Fix of the issue that scrollbar in call log didn't represent the actual page being shown correctly.
278 lines
9.3 KiB
C++
278 lines
9.3 KiB
C++
// Copyright (c) 2017-2023, Mudita Sp. z.o.o. All rights reserved.
|
|
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
|
|
|
|
#include "ListView.hpp"
|
|
#include "InputEvent.hpp"
|
|
|
|
namespace gui
|
|
{
|
|
ListViewScroll::ListViewScroll(
|
|
Item *parent, unsigned int x, unsigned int y, unsigned int w, unsigned int h, listview::ScrollBarType type)
|
|
: Rect{parent, x, y, w, h}, type(type)
|
|
{
|
|
|
|
setRadius(style::listview::scroll::radius);
|
|
setFilled(true);
|
|
setFillColor(style::listview::scroll::color);
|
|
activeItem = false;
|
|
}
|
|
|
|
void ListViewScroll::updateProportional(const ListViewScrollUpdateData &data)
|
|
{
|
|
const auto scrollStep =
|
|
static_cast<double>((parent->widgetArea.h - topMargin)) / static_cast<double>(data.elementsCount);
|
|
|
|
const auto scrollH = scrollStep * data.listPageSize;
|
|
const auto scrollY = (scrollStep * data.startIndex > 0) ? scrollStep * data.startIndex : topMargin;
|
|
|
|
setArea(BoundingBox(
|
|
parent->widgetArea.w - style::listview::scroll::margin, scrollY, style::listview::scroll::w, scrollH));
|
|
}
|
|
|
|
void ListViewScroll::updateFixed(const ListViewScrollUpdateData &data)
|
|
{
|
|
const auto elementsOnPage = parent->widgetArea.h / data.elementMinimalSpaceRequired;
|
|
|
|
pagesCount = (data.elementsCount % elementsOnPage == 0) ? data.elementsCount / elementsOnPage
|
|
: data.elementsCount / elementsOnPage + 1;
|
|
|
|
currentPage = data.startIndex / elementsOnPage;
|
|
|
|
const auto scrollH = (parent->widgetArea.h - topMargin) / pagesCount;
|
|
const auto scrollY = (scrollH * currentPage > 0) ? scrollH * currentPage : topMargin;
|
|
|
|
setArea(BoundingBox(
|
|
parent->widgetArea.w - style::listview::scroll::margin, scrollY, style::listview::scroll::w, scrollH));
|
|
}
|
|
|
|
void ListViewScroll::updatePreRendered(const ListViewScrollUpdateData &data)
|
|
{
|
|
if (data.startIndex != storedStartIndex) {
|
|
if (data.direction == listview::Direction::Bottom) {
|
|
if (data.boundaries == Boundaries::Continuous && (data.startIndex == 0)) {
|
|
currentPage = 0;
|
|
}
|
|
else if (currentPage + 1 < pagesCount) {
|
|
currentPage++;
|
|
}
|
|
}
|
|
else {
|
|
if (data.boundaries == Boundaries::Continuous && storedStartIndex == 0) {
|
|
currentPage = pagesCount - 1;
|
|
}
|
|
else if (currentPage > 0 && storedStartIndex != 0) {
|
|
currentPage--;
|
|
}
|
|
}
|
|
}
|
|
|
|
storedStartIndex = data.startIndex;
|
|
|
|
auto scrollH = parent->widgetArea.h / pagesCount;
|
|
auto scrollY = currentPage * scrollH > 0 ? currentPage * scrollH : topMargin;
|
|
|
|
setArea(BoundingBox(
|
|
parent->widgetArea.w - style::listview::scroll::margin, scrollY, style::listview::scroll::w, scrollH));
|
|
}
|
|
|
|
void ListViewScroll::updateStartConditions(const ListViewScrollSetupData &data)
|
|
{
|
|
storedStartIndex = data.storedStartIndex;
|
|
currentPage = data.currentPage;
|
|
pagesCount = data.pagesCount;
|
|
}
|
|
|
|
bool ListViewScroll::shouldShowScroll(unsigned int currentPageSize, unsigned int elementsCount)
|
|
{
|
|
return ((parent->widgetArea.w > style::listview::scroll::min_space) &&
|
|
(parent->widgetArea.h > style::listview::scroll::min_space) && currentPageSize < elementsCount);
|
|
}
|
|
|
|
void ListViewScroll::update(const ListViewScrollUpdateData &data)
|
|
{
|
|
if (shouldShowScroll(data.listPageSize, data.elementsCount)) {
|
|
|
|
switch (type) {
|
|
case listview::ScrollBarType::Proportional:
|
|
updateProportional(data);
|
|
break;
|
|
case listview::ScrollBarType::Fixed:
|
|
updateFixed(data);
|
|
break;
|
|
case listview::ScrollBarType::PreRendered:
|
|
updatePreRendered(data);
|
|
break;
|
|
case listview::ScrollBarType::None:
|
|
break;
|
|
}
|
|
|
|
setVisible(true);
|
|
}
|
|
else {
|
|
setVisible(false);
|
|
}
|
|
}
|
|
void ListViewScroll::setTopMargin(int _topMargin)
|
|
{
|
|
topMargin = _topMargin;
|
|
}
|
|
|
|
ListView::ListView(Item *parent,
|
|
unsigned int x,
|
|
unsigned int y,
|
|
unsigned int w,
|
|
unsigned int h,
|
|
std::shared_ptr<ListItemProvider> prov,
|
|
listview::ScrollBarType scrollBarType)
|
|
: Rect{parent, x, y, w, h}, ListViewEngine(std::move(prov))
|
|
{
|
|
this->setEdges(RectangleEdge::None);
|
|
|
|
body = new VBox{this, 0, 0, w, h};
|
|
body->setAlignment(Alignment::Vertical::Top);
|
|
body->setEdges(RectangleEdge::None);
|
|
|
|
body->borderCallback = [this](const InputEvent &inputEvent) -> bool {
|
|
if (!inputEvent.isShortRelease()) {
|
|
return false;
|
|
}
|
|
if (inputEvent.is(KeyCode::KEY_UP) && pageLoaded) {
|
|
return this->requestPreviousPage();
|
|
}
|
|
else if (inputEvent.is(KeyCode::KEY_DOWN) && pageLoaded) {
|
|
return this->requestNextPage();
|
|
}
|
|
return false;
|
|
};
|
|
|
|
inputCallback = [&]([[maybe_unused]] Item &item, const InputEvent &event) { return body->onInput(event); };
|
|
|
|
focusChangedCallback = [this]([[maybe_unused]] Item &item) -> bool {
|
|
if (focus) {
|
|
setFocus();
|
|
}
|
|
else {
|
|
setFocusItem(nullptr);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
body->parentOnRequestedResizeCallback = [this]() {
|
|
if (pageLoaded) {
|
|
recalculateOnBoxRequestedResize();
|
|
}
|
|
};
|
|
|
|
checkFullRenderRequirementCallback = [this]() {
|
|
if (scroll && scroll->type == listview::ScrollBarType::PreRendered) {
|
|
requestFullListRender = true;
|
|
}
|
|
};
|
|
|
|
if (scrollBarType != listview::ScrollBarType::None) {
|
|
scroll = new ListViewScroll(this,
|
|
style::listview::scroll::x,
|
|
style::listview::scroll::y,
|
|
style::listview::scroll::w,
|
|
style::listview::scroll::h,
|
|
scrollBarType);
|
|
}
|
|
determineFetchType(scrollBarType);
|
|
applyScrollCallbacks();
|
|
|
|
type = gui::ItemType::LIST;
|
|
}
|
|
|
|
void ListView::applyScrollCallbacks()
|
|
{
|
|
setupScrollCallback = [this](ListViewScrollSetupData data) {
|
|
if (scroll) {
|
|
scroll->updateStartConditions(data);
|
|
}
|
|
};
|
|
|
|
updateScrollCallback = [this](ListViewScrollUpdateData data) {
|
|
if (scroll) {
|
|
scroll->update(data);
|
|
}
|
|
};
|
|
|
|
resizeScrollCallback = [this]() {
|
|
if (scroll && scroll->shouldShowScroll(currentPageSize, elementsCount)) {
|
|
body->setSize(style::listview::item_width_with_scroll, body->getHeight());
|
|
}
|
|
else {
|
|
body->setSize(style::window::default_body_width, body->getHeight());
|
|
}
|
|
};
|
|
}
|
|
|
|
void ListView::determineFetchType(listview::ScrollBarType scrollType)
|
|
{
|
|
switch (scrollType) {
|
|
case listview::ScrollBarType::None:
|
|
case listview::ScrollBarType::Fixed:
|
|
fetchType = listview::FetchType::Fixed;
|
|
break;
|
|
case listview::ScrollBarType::Proportional:
|
|
case listview::ScrollBarType::PreRendered:
|
|
fetchType = listview::FetchType::Filled;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ListView::clear()
|
|
{
|
|
if (scroll) {
|
|
scroll->setVisible(false);
|
|
}
|
|
ListViewEngine::clear();
|
|
}
|
|
|
|
void ListView::setFocus()
|
|
{
|
|
if (!focus) {
|
|
return;
|
|
}
|
|
setFocusItem(body);
|
|
|
|
ListViewEngine::setFocus();
|
|
}
|
|
|
|
void ListView::setScrollTopMargin(int value)
|
|
{
|
|
if (scroll) {
|
|
scroll->setTopMargin(value);
|
|
}
|
|
}
|
|
|
|
void ListView::setAlignment(const Alignment &value)
|
|
{
|
|
if (body->getAlignment() != value) {
|
|
body->setAlignment(value);
|
|
}
|
|
}
|
|
|
|
bool ListView::onDimensionChanged(const BoundingBox &oldDim, const BoundingBox &newDim)
|
|
{
|
|
Rect::onDimensionChanged(oldDim, newDim);
|
|
body->setSize(body->getWidth(), newDim.h);
|
|
|
|
if (updateScrollCallback) {
|
|
updateScrollCallback(ListViewScrollUpdateData{startIndex,
|
|
currentPageSize,
|
|
elementsCount,
|
|
provider->getMinimalItemSpaceRequired(),
|
|
direction,
|
|
boundaries});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
auto ListView::handleRequestResize([[maybe_unused]] const Item *child, Length request_w, Length request_h) -> Size
|
|
{
|
|
return Size(request_w, request_h);
|
|
}
|
|
} /* namespace gui */
|