mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-26 23:10:38 +00:00
578 lines
16 KiB
C++
578 lines
16 KiB
C++
#include <algorithm>
|
|
#include "Common/UI/Context.h"
|
|
#include "Common/UI/ScrollView.h"
|
|
#include "Common/Data/Text/I18n.h"
|
|
#include "Common/Log.h"
|
|
|
|
namespace UI {
|
|
|
|
float ScrollView::lastScrollPosX = 0;
|
|
float ScrollView::lastScrollPosY = 0;
|
|
|
|
ScrollView::~ScrollView() {
|
|
lastScrollPosX = 0;
|
|
lastScrollPosY = 0;
|
|
}
|
|
|
|
void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) {
|
|
// Respect margins
|
|
Margins margins;
|
|
if (views_.size()) {
|
|
const LinearLayoutParams *linLayoutParams = views_[0]->GetLayoutParams()->As<LinearLayoutParams>();
|
|
if (linLayoutParams) {
|
|
margins = linLayoutParams->margins;
|
|
}
|
|
}
|
|
|
|
// The scroll view itself simply obeys its parent - but also tries to fit the child if possible.
|
|
MeasureBySpec(layoutParams_->width, horiz.size, horiz, &measuredWidth_);
|
|
MeasureBySpec(layoutParams_->height, vert.size, vert, &measuredHeight_);
|
|
|
|
if (views_.size()) {
|
|
if (orientation_ == ORIENT_HORIZONTAL) {
|
|
MeasureSpec v = MeasureSpec(AT_MOST, measuredHeight_ - margins.vert());
|
|
if (measuredHeight_ == 0.0f && (vert.type == UNSPECIFIED || layoutParams_->height == WRAP_CONTENT)) {
|
|
v.type = UNSPECIFIED;
|
|
}
|
|
views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED, measuredWidth_), v);
|
|
MeasureBySpec(layoutParams_->height, views_[0]->GetMeasuredHeight(), vert, &measuredHeight_);
|
|
if (layoutParams_->width == WRAP_CONTENT)
|
|
MeasureBySpec(layoutParams_->width, views_[0]->GetMeasuredWidth(), horiz, &measuredWidth_);
|
|
} else {
|
|
MeasureSpec h = MeasureSpec(AT_MOST, measuredWidth_ - margins.horiz());
|
|
if (measuredWidth_ == 0.0f && (horiz.type == UNSPECIFIED || layoutParams_->width == WRAP_CONTENT)) {
|
|
h.type = UNSPECIFIED;
|
|
}
|
|
views_[0]->Measure(dc, h, MeasureSpec(UNSPECIFIED, measuredHeight_));
|
|
MeasureBySpec(layoutParams_->width, views_[0]->GetMeasuredWidth(), horiz, &measuredWidth_);
|
|
if (layoutParams_->height == WRAP_CONTENT)
|
|
MeasureBySpec(layoutParams_->height, views_[0]->GetMeasuredHeight(), vert, &measuredHeight_);
|
|
}
|
|
if (orientation_ == ORIENT_VERTICAL && vert.type != EXACTLY) {
|
|
float bestHeight = std::max(views_[0]->GetMeasuredHeight(), views_[0]->GetBounds().h);
|
|
if (vert.type == AT_MOST)
|
|
bestHeight = std::min(bestHeight, vert.size);
|
|
|
|
if (measuredHeight_ < bestHeight && layoutParams_->height < 0.0f) {
|
|
measuredHeight_ = bestHeight;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScrollView::Layout() {
|
|
if (!views_.size())
|
|
return;
|
|
Bounds scrolled;
|
|
|
|
// Respect margins
|
|
Margins margins;
|
|
const LinearLayoutParams *linLayoutParams = views_[0]->GetLayoutParams()->As<LinearLayoutParams>();
|
|
if (linLayoutParams) {
|
|
margins = linLayoutParams->margins;
|
|
}
|
|
|
|
scrolled.w = views_[0]->GetMeasuredWidth() - margins.horiz();
|
|
scrolled.h = views_[0]->GetMeasuredHeight() - margins.vert();
|
|
|
|
layoutScrollPos_ = ClampedScrollPos(scrollPos_);
|
|
|
|
switch (orientation_) {
|
|
case ORIENT_HORIZONTAL:
|
|
if (scrolled.w != lastViewSize_) {
|
|
if (rememberPos_)
|
|
scrollPos_ = *rememberPos_;
|
|
lastViewSize_ = scrolled.w;
|
|
}
|
|
scrolled.x = bounds_.x - layoutScrollPos_;
|
|
scrolled.y = bounds_.y + margins.top;
|
|
break;
|
|
case ORIENT_VERTICAL:
|
|
if (scrolled.h != lastViewSize_) {
|
|
if (rememberPos_)
|
|
scrollPos_ = *rememberPos_;
|
|
lastViewSize_ = scrolled.h;
|
|
}
|
|
scrolled.x = bounds_.x + margins.left;
|
|
scrolled.y = bounds_.y - layoutScrollPos_;
|
|
break;
|
|
}
|
|
|
|
views_[0]->SetBounds(scrolled);
|
|
views_[0]->Layout();
|
|
}
|
|
|
|
bool ScrollView::Key(const KeyInput &input) {
|
|
if (visibility_ != V_VISIBLE)
|
|
return ViewGroup::Key(input);
|
|
|
|
float scrollSpeed = 250;
|
|
switch (input.deviceId) {
|
|
case DEVICE_ID_XR_CONTROLLER_LEFT:
|
|
case DEVICE_ID_XR_CONTROLLER_RIGHT:
|
|
scrollSpeed = 50;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (input.flags & KEY_DOWN) {
|
|
if ((input.keyCode == NKCODE_EXT_MOUSEWHEEL_UP || input.keyCode == NKCODE_EXT_MOUSEWHEEL_DOWN) &&
|
|
(input.flags & KEY_HASWHEELDELTA)) {
|
|
scrollSpeed = (float)(short)(input.flags >> 16) * 1.25f; // Fudge factor. TODO: Should be moved to the backends.
|
|
}
|
|
|
|
switch (input.keyCode) {
|
|
case NKCODE_EXT_MOUSEWHEEL_UP:
|
|
ScrollRelative(-scrollSpeed);
|
|
break;
|
|
case NKCODE_EXT_MOUSEWHEEL_DOWN:
|
|
ScrollRelative(scrollSpeed);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return ViewGroup::Key(input);
|
|
}
|
|
|
|
const float friction = 0.92f;
|
|
const float stop_threshold = 0.1f;
|
|
|
|
bool ScrollView::Touch(const TouchInput &input) {
|
|
if ((input.flags & TOUCH_DOWN) && scrollTouchId_ == -1 && bounds_.Contains(input.x, input.y)) {
|
|
if (orientation_ == ORIENT_VERTICAL) {
|
|
Bob bob = ComputeBob();
|
|
float internalY = input.y - bounds_.y;
|
|
float bobMargin = 3.0f; // Add some extra margin for the touch.
|
|
draggingBob_ = internalY >= bob.offset - bobMargin && internalY <= bob.offset + bob.size + bobMargin && input.x >= bounds_.x2() - 20.0f;
|
|
barDragStart_ = bob.offset;
|
|
barDragOffset_ = internalY - bob.offset;
|
|
}
|
|
|
|
scrollStart_ = scrollPos_;
|
|
inertia_ = 0.0f;
|
|
scrollTouchId_ = input.id;
|
|
}
|
|
|
|
Gesture gesture = orientation_ == ORIENT_VERTICAL ? GESTURE_DRAG_VERTICAL : GESTURE_DRAG_HORIZONTAL;
|
|
|
|
if ((input.flags & TOUCH_UP) && input.id == scrollTouchId_) {
|
|
float info[4];
|
|
if (gesture_.GetGestureInfo(gesture, input.id, info)) {
|
|
inertia_ = info[1];
|
|
}
|
|
scrollTouchId_ = -1;
|
|
draggingBob_ = false;
|
|
}
|
|
|
|
// We modify the input2 we send to children, so we can cancel drags if we start scrolling, and stuff like that.
|
|
TouchInput input2;
|
|
if (CanScroll()) {
|
|
if (draggingBob_) {
|
|
// Cancel any drags/holds on the children instantly to avoid accidental click-throughs.
|
|
input2.flags = TOUCH_UP | TOUCH_CANCEL;
|
|
// Skip the gesture manager, do calculations directly.
|
|
// Might switch to the gesture later.
|
|
Bob bob = ComputeBob();
|
|
float internalY = input.y - bounds_.y;
|
|
|
|
float bobPos = internalY - barDragOffset_;
|
|
float bobDragMax = bounds_.h - bob.size;
|
|
|
|
float newScrollPos = Clamp(bobPos / bobDragMax, 0.0f, 1.0f) * bob.scrollMax;
|
|
|
|
scrollPos_ = newScrollPos;
|
|
scrollTarget_ = newScrollPos;
|
|
scrollToTarget_ = false;
|
|
} else {
|
|
input2 = gesture_.Update(input, bounds_);
|
|
float info[4];
|
|
if (input.id == scrollTouchId_ && gesture_.GetGestureInfo(gesture, input.id, info) && !(input.flags & TOUCH_DOWN)) {
|
|
float pos = scrollStart_ - info[0];
|
|
scrollPos_ = pos;
|
|
scrollTarget_ = pos;
|
|
scrollToTarget_ = false;
|
|
}
|
|
}
|
|
} else {
|
|
input2 = input;
|
|
scrollTarget_ = scrollPos_;
|
|
scrollToTarget_ = false;
|
|
}
|
|
|
|
if (!(input.flags & TOUCH_DOWN) || bounds_.Contains(input.x, input.y)) {
|
|
return ViewGroup::Touch(input2);
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ScrollView::Bob ScrollView::ComputeBob() const {
|
|
Bob bob{};
|
|
if (views_.empty()) {
|
|
return bob;
|
|
}
|
|
float childHeight = std::max(0.01f, views_[0]->GetBounds().h);
|
|
float scrollMax = std::max(0.0f, childHeight - bounds_.h);
|
|
float ratio = bounds_.h / childHeight;
|
|
|
|
if (ratio < 1.0f && scrollMax > 0.0f) {
|
|
bob.show = true;
|
|
bob.thickness = draggingBob_ ? 15.0f : 6.0f;
|
|
bob.size = ratio * bounds_.h;
|
|
bob.offset = (HardClampedScrollPos(scrollPos_) / scrollMax) * (bounds_.h - bob.size);
|
|
bob.scrollMax = scrollMax;
|
|
}
|
|
return bob;
|
|
}
|
|
|
|
void ScrollView::Draw(UIContext &dc) {
|
|
if (!views_.size()) {
|
|
ViewGroup::Draw(dc);
|
|
return;
|
|
}
|
|
|
|
dc.PushScissor(bounds_);
|
|
dc.FillRect(bg_, bounds_);
|
|
|
|
// For debugging layout issues, this can be useful.
|
|
// dc.FillRect(Drawable(0x60FF00FF), bounds_);
|
|
views_[0]->Draw(dc);
|
|
dc.PopScissor();
|
|
|
|
// Vertical scroll bob. We don't support a horizontal yet.
|
|
if (orientation_ != ORIENT_VERTICAL) {
|
|
return;
|
|
}
|
|
|
|
Bob bob = ComputeBob();
|
|
|
|
if (bob.show) {
|
|
Bounds bobBounds(bounds_.x2() - bob.thickness, bounds_.y + bob.offset, bob.thickness, bob.size);
|
|
dc.FillRect(Drawable(0x80FFFFFF), bobBounds);
|
|
}
|
|
}
|
|
|
|
bool ScrollView::SubviewFocused(View *view) {
|
|
if (!ViewGroup::SubviewFocused(view))
|
|
return false;
|
|
|
|
const Bounds &vBounds = view->GetBounds();
|
|
|
|
// Scroll so that the focused view is visible, and a bit more so that headers etc gets visible too, in most cases.
|
|
const float overscroll = std::min(view->GetBounds().h / 1.5f, GetBounds().h / 4.0f);
|
|
|
|
float pos = ClampedScrollPos(scrollPos_);
|
|
float visibleSize = orientation_ == ORIENT_VERTICAL ? bounds_.h : bounds_.w;
|
|
float visibleEnd = scrollPos_ + visibleSize;
|
|
|
|
float viewStart = 0.0f, viewEnd = 0.0f;
|
|
switch (orientation_) {
|
|
case ORIENT_HORIZONTAL:
|
|
viewStart = layoutScrollPos_ + vBounds.x - bounds_.x;
|
|
viewEnd = layoutScrollPos_ + vBounds.x2() - bounds_.x;
|
|
break;
|
|
case ORIENT_VERTICAL:
|
|
viewStart = layoutScrollPos_ + vBounds.y - bounds_.y;
|
|
viewEnd = layoutScrollPos_ + vBounds.y2() - bounds_.y;
|
|
break;
|
|
}
|
|
|
|
if (viewEnd > visibleEnd) {
|
|
ScrollTo(viewEnd - visibleSize + overscroll);
|
|
} else if (viewStart < pos) {
|
|
ScrollTo(viewStart - overscroll);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NeighborResult ScrollView::FindScrollNeighbor(View *view, const Point2D &target, FocusDirection direction, NeighborResult best) {
|
|
if (ContainsSubview(view) && views_[0]->IsViewGroup()) {
|
|
ViewGroup *vg = static_cast<ViewGroup *>(views_[0]);
|
|
int found = -1;
|
|
for (int i = 0, n = vg->GetNumSubviews(); i < n; ++i) {
|
|
View *child = vg->GetViewByIndex(i);
|
|
if (child == view || child->ContainsSubview(view)) {
|
|
found = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Okay, the previously focused view is inside this.
|
|
if (found != -1) {
|
|
float mult = 0.0f;
|
|
switch (direction) {
|
|
case FOCUS_PREV_PAGE:
|
|
mult = -1.0f;
|
|
break;
|
|
case FOCUS_NEXT_PAGE:
|
|
mult = 1.0f;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Okay, now where is our ideal target?
|
|
Point2D targetPos = view->GetBounds().Center();
|
|
if (orientation_ == ORIENT_VERTICAL)
|
|
targetPos.y += mult * bounds_.h;
|
|
else
|
|
targetPos.x += mult * bounds_.x;
|
|
|
|
// Okay, which subview is closest to that?
|
|
best = vg->FindScrollNeighbor(view, targetPos, direction, best);
|
|
// Avoid reselecting the same view.
|
|
if (best.view == view)
|
|
best.view = nullptr;
|
|
return best;
|
|
}
|
|
}
|
|
|
|
return ViewGroup::FindScrollNeighbor(view, target, direction, best);
|
|
}
|
|
|
|
void ScrollView::PersistData(PersistStatus status, std::string anonId, PersistMap &storage) {
|
|
ViewGroup::PersistData(status, anonId, storage);
|
|
|
|
std::string tag = Tag();
|
|
if (tag.empty()) {
|
|
tag = anonId;
|
|
}
|
|
|
|
PersistBuffer &buffer = storage["ScrollView::" + tag];
|
|
switch (status) {
|
|
case PERSIST_SAVE:
|
|
{
|
|
buffer.resize(1);
|
|
float pos = scrollToTarget_ ? scrollTarget_ : scrollPos_;
|
|
// Hmm, ugly... better buffer?
|
|
buffer[0] = *(int *)&pos;
|
|
}
|
|
break;
|
|
|
|
case PERSIST_RESTORE:
|
|
if (buffer.size() == 1) {
|
|
float pos = *(float *)&buffer[0];
|
|
scrollPos_ = pos;
|
|
scrollTarget_ = pos;
|
|
scrollToTarget_ = false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ScrollView::SetVisibility(Visibility visibility) {
|
|
ViewGroup::SetVisibility(visibility);
|
|
|
|
if (visibility == V_GONE && !rememberPos_) {
|
|
// Since this is no longer shown, forget the scroll position.
|
|
// For example, this happens when switching tabs.
|
|
ScrollTo(0.0f);
|
|
}
|
|
}
|
|
|
|
void ScrollView::ScrollTo(float newScrollPos) {
|
|
scrollTarget_ = newScrollPos;
|
|
scrollToTarget_ = true;
|
|
}
|
|
|
|
void ScrollView::ScrollRelative(float distance) {
|
|
scrollTarget_ = scrollPos_ + distance;
|
|
scrollToTarget_ = true;
|
|
}
|
|
|
|
float ScrollView::HardClampedScrollPos(float pos) const {
|
|
if (!views_.size() || bounds_.h == 0.0f) {
|
|
return 0.0f;
|
|
}
|
|
float childSize = orientation_ == ORIENT_VERTICAL ? views_[0]->GetBounds().h : views_[0]->GetBounds().w;
|
|
float containerSize = (orientation_ == ORIENT_VERTICAL ? bounds_.h : bounds_.w);
|
|
float scrollMax = std::max(0.0f, childSize - containerSize);
|
|
return Clamp(pos, 0.0f, scrollMax);
|
|
}
|
|
|
|
float ScrollView::ClampedScrollPos(float pos) {
|
|
if (!views_.size() || bounds_.h == 0.0f) {
|
|
return 0.0f;
|
|
}
|
|
|
|
float childSize = orientation_ == ORIENT_VERTICAL ? views_[0]->GetBounds().h : views_[0]->GetBounds().w;
|
|
float containerSize = (orientation_ == ORIENT_VERTICAL ? bounds_.h : bounds_.w);
|
|
float scrollMax = std::max(0.0f, childSize - containerSize);
|
|
|
|
Gesture gesture = orientation_ == ORIENT_VERTICAL ? GESTURE_DRAG_VERTICAL : GESTURE_DRAG_HORIZONTAL;
|
|
|
|
// TODO: Not all of this is properly orientation independent.
|
|
if (scrollTouchId_ >= 0 && gesture_.IsGestureActive(gesture, scrollTouchId_) && bounds_.h > 0.0f) {
|
|
float maxPull = bounds_.h * 0.1f;
|
|
if (pos < 0.0f) {
|
|
float dist = std::min(-pos * (1.0f / bounds_.h), 1.0f);
|
|
pull_ = -(sqrt(dist) * maxPull);
|
|
} else if (pos > scrollMax) {
|
|
float dist = std::min((pos - scrollMax) * (1.0f / bounds_.h), 1.0f);
|
|
pull_ = sqrt(dist) * maxPull;
|
|
} else {
|
|
pull_ = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (pos < 0.0f && pos < pull_) {
|
|
pos = pull_;
|
|
}
|
|
if (pos > scrollMax && pos > scrollMax + pull_) {
|
|
pos = scrollMax + pull_;
|
|
}
|
|
if (childSize < containerSize &&alignOpposite_) {
|
|
pos = -(containerSize - childSize);
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
void ScrollView::ScrollToBottom() {
|
|
float childHeight = views_[0]->GetBounds().h;
|
|
float scrollMax = std::max(0.0f, childHeight - bounds_.h);
|
|
scrollPos_ = scrollMax;
|
|
scrollTarget_ = scrollMax;
|
|
}
|
|
|
|
bool ScrollView::CanScroll() const {
|
|
if (!views_.size())
|
|
return false;
|
|
switch (orientation_) {
|
|
case ORIENT_VERTICAL:
|
|
return views_[0]->GetBounds().h > bounds_.h;
|
|
case ORIENT_HORIZONTAL:
|
|
return views_[0]->GetBounds().w > bounds_.w;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ScrollView::GetLastScrollPosition(float &x, float &y) {
|
|
x = lastScrollPosX;
|
|
y = lastScrollPosY;
|
|
}
|
|
|
|
void ScrollView::Update() {
|
|
if (visibility_ != V_VISIBLE) {
|
|
inertia_ = 0.0f;
|
|
}
|
|
ViewGroup::Update();
|
|
float oldPos = scrollPos_;
|
|
|
|
Gesture gesture = orientation_ == ORIENT_VERTICAL ? GESTURE_DRAG_VERTICAL : GESTURE_DRAG_HORIZONTAL;
|
|
gesture_.UpdateFrame();
|
|
if (scrollToTarget_) {
|
|
float target = ClampedScrollPos(scrollTarget_);
|
|
|
|
inertia_ = 0.0f;
|
|
if (fabsf(target - scrollPos_) < 0.5f) {
|
|
scrollPos_ = target;
|
|
scrollToTarget_ = false;
|
|
} else {
|
|
scrollPos_ += (target - scrollPos_) * 0.3f;
|
|
}
|
|
} else if (inertia_ != 0.0f && !gesture_.IsGestureActive(gesture, scrollTouchId_)) {
|
|
scrollPos_ -= inertia_;
|
|
inertia_ *= friction;
|
|
if (fabsf(inertia_) < stop_threshold)
|
|
inertia_ = 0.0f;
|
|
}
|
|
|
|
if (!gesture_.IsGestureActive(gesture, scrollTouchId_)) {
|
|
scrollPos_ = ClampedScrollPos(scrollPos_);
|
|
|
|
pull_ *= friction;
|
|
if (fabsf(pull_) < 0.01f) {
|
|
pull_ = 0.0f;
|
|
}
|
|
}
|
|
|
|
if (oldPos != scrollPos_)
|
|
orientation_ == ORIENT_HORIZONTAL ? lastScrollPosX = scrollPos_ : lastScrollPosY = scrollPos_;
|
|
|
|
// We load some lists asynchronously, so don't update the position until it's loaded.
|
|
if (rememberPos_ && ClampedScrollPos(scrollPos_) != ClampedScrollPos(*rememberPos_)) {
|
|
*rememberPos_ = scrollPos_;
|
|
}
|
|
}
|
|
|
|
ListView::ListView(ListAdaptor *a, std::set<int> hidden, std::map<int, ImageID> icons, LayoutParams *layoutParams)
|
|
: ScrollView(ORIENT_VERTICAL, layoutParams), adaptor_(a), maxHeight_(0), hidden_(hidden), icons_(icons) {
|
|
linLayout_ = new LinearLayout(ORIENT_VERTICAL);
|
|
linLayout_->SetSpacing(0.0f);
|
|
Add(linLayout_);
|
|
CreateAllItems();
|
|
}
|
|
|
|
void ListView::CreateAllItems() {
|
|
linLayout_->Clear();
|
|
// Let's not be clever yet, we'll just create them all up front and add them all in.
|
|
for (int i = 0; i < adaptor_->GetNumItems(); i++) {
|
|
if (hidden_.find(i) == hidden_.end()) {
|
|
ImageID *imageID = nullptr;
|
|
auto iter = icons_.find(i);
|
|
if (iter != icons_.end()) {
|
|
imageID = &iter->second;
|
|
}
|
|
View *v = linLayout_->Add(adaptor_->CreateItemView(i, imageID));
|
|
adaptor_->AddEventCallback(v, std::bind(&ListView::OnItemCallback, this, i, std::placeholders::_1));
|
|
}
|
|
}
|
|
}
|
|
|
|
void ListView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) {
|
|
ScrollView::Measure(dc, horiz, vert);
|
|
if (maxHeight_ > 0 && measuredHeight_ > maxHeight_) {
|
|
measuredHeight_ = maxHeight_;
|
|
}
|
|
}
|
|
|
|
std::string ListView::DescribeText() const {
|
|
auto u = GetI18NCategory(I18NCat::UI_ELEMENTS);
|
|
return DescribeListOrdered(u->T("List:"));
|
|
}
|
|
|
|
EventReturn ListView::OnItemCallback(int num, EventParams &e) {
|
|
EventParams ev{};
|
|
ev.v = nullptr;
|
|
ev.a = num;
|
|
adaptor_->SetSelected(num);
|
|
OnChoice.Trigger(ev);
|
|
CreateAllItems();
|
|
return EVENT_DONE;
|
|
}
|
|
|
|
View *ChoiceListAdaptor::CreateItemView(int index, ImageID *optionalImageID) {
|
|
Choice *choice = new Choice(items_[index]);
|
|
if (optionalImageID) {
|
|
choice->SetIcon(*optionalImageID);
|
|
}
|
|
return choice;
|
|
}
|
|
|
|
bool ChoiceListAdaptor::AddEventCallback(View *view, std::function<EventReturn(EventParams &)> callback) {
|
|
Choice *choice = (Choice *)view;
|
|
choice->OnClick.Add(callback);
|
|
return EVENT_DONE;
|
|
}
|
|
|
|
|
|
View *StringVectorListAdaptor::CreateItemView(int index, ImageID *optionalImageID) {
|
|
Choice *choice = new Choice(items_[index], "", index == selected_);
|
|
if (optionalImageID) {
|
|
choice->SetIcon(*optionalImageID);
|
|
}
|
|
return choice;
|
|
}
|
|
|
|
bool StringVectorListAdaptor::AddEventCallback(View *view, std::function<EventReturn(EventParams &)> callback) {
|
|
Choice *choice = (Choice *)view;
|
|
choice->OnClick.Add(callback);
|
|
return EVENT_DONE;
|
|
}
|
|
|
|
} // namespace
|