diff --git a/ui/screen.cpp b/ui/screen.cpp index 63774199e6..7bd05e619c 100644 --- a/ui/screen.cpp +++ b/ui/screen.cpp @@ -95,8 +95,7 @@ void ScreenManager::sendMessage(const char *msg, const char *value) { stack_.back().screen->sendMessage(msg, value); } -void ScreenManager::deviceLost() -{ +void ScreenManager::deviceLost() { if (!stack_.empty()) stack_.back().screen->deviceLost(); // Dialogs too? Nah, they should only use the standard UI texture anyway. @@ -124,6 +123,9 @@ void ScreenManager::push(Screen *screen, int layerFlags) { switchToNext(); } screen->setScreenManager(this); + if (screen->isTransparent()) { + layerFlags |= LAYER_TRANSPARENT; + } Layer layer = {screen, layerFlags}; stack_.push_back(layer); } @@ -157,7 +159,11 @@ void ScreenManager::processFinishDialog() { } Screen *caller = topScreen(); - caller->dialogFinished(dialogFinished_, dialogResult_); + if (caller) { + caller->dialogFinished(dialogFinished_, dialogResult_); + } else { + ELOG("ERROR: no top screen when finishing dialog"); + } delete dialogFinished_; dialogFinished_ = 0; } diff --git a/ui/screen.h b/ui/screen.h index ca2983dae2..80c8d36650 100644 --- a/ui/screen.h +++ b/ui/screen.h @@ -51,6 +51,8 @@ public: virtual void *dialogData() { return 0; } + virtual bool isTransparent() { return false; } + private: ScreenManager *screenManager_; DISALLOW_COPY_AND_ASSIGN(Screen); diff --git a/ui/ui_screen.cpp b/ui/ui_screen.cpp index b55f590c85..8b913e8165 100644 --- a/ui/ui_screen.cpp +++ b/ui/ui_screen.cpp @@ -24,7 +24,7 @@ void UIScreen::render() { UI::LayoutViewHierarchy(*screenManager()->getUIContext(), root_); screenManager()->getUIContext()->Begin(); - DrawBackground(); + DrawBackground(*screenManager()->getUIContext()); root_->Draw(*screenManager()->getUIContext()); screenManager()->getUIContext()->End(); screenManager()->getUIContext()->Flush(); diff --git a/ui/ui_screen.h b/ui/ui_screen.h index bcf2ab2297..ff5dab0255 100644 --- a/ui/ui_screen.h +++ b/ui/ui_screen.h @@ -17,7 +17,7 @@ public: protected: virtual void CreateViews() = 0; - virtual void DrawBackground() {} + virtual void DrawBackground(UIContext &dc) {} void RecreateViews() { recreateViews_ = true; } diff --git a/ui/view.cpp b/ui/view.cpp index 1f57254942..87df3a150e 100644 --- a/ui/view.cpp +++ b/ui/view.cpp @@ -154,8 +154,19 @@ Item::Item(LayoutParams *layoutParams) : InertView(layoutParams) { layoutParams_->height = ITEM_HEIGHT; } +void Item::GetContentDimensions(const UIContext &dc, float &w, float &h) const { + w = 0.0f; + h = 0.0f; +} + +void ClickableItem::GetContentDimensions(const UIContext &dc, float &w, float &h) const { + w = 0.0f; + h = 0.0f; +} + ClickableItem::ClickableItem(LayoutParams *layoutParams) : Clickable(layoutParams) { - layoutParams_->width = FILL_PARENT; + if (layoutParams_->width == WRAP_CONTENT) + layoutParams_->width = FILL_PARENT; layoutParams_->height = ITEM_HEIGHT; } diff --git a/ui/view.h b/ui/view.h index c371122f4c..bbe0b8b374 100644 --- a/ui/view.h +++ b/ui/view.h @@ -183,14 +183,16 @@ public: // Call this from UI thread void Update(); + // This is suggested for use in most cases. Autobinds, allowing for neat syntax. template void Handle(T *thiz, EventReturn (T::* theCallback)(EventParams &e)) { Add(std::bind(theCallback, thiz, placeholder::_1)); } -private: + // Sometimes you have an already-bound function<>, just use this then. void Add(std::function func); +private: recursive_mutex mutex_; std::vector handlers_; bool triggered_; @@ -381,19 +383,13 @@ private: class Item : public InertView { public: Item(LayoutParams *layoutParams); - virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const { - w = 0.0f; - h = 0.0f; - } + virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const; }; class ClickableItem : public Clickable { public: ClickableItem(LayoutParams *layoutParams); - virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const { - w = 0.0f; - h = 0.0f; - } + virtual void GetContentDimensions(const UIContext &dc, float &w, float &h) const; // Draws the item background. virtual void Draw(UIContext &dc); diff --git a/ui/viewgroup.cpp b/ui/viewgroup.cpp index 00f314043c..e93b070cab 100644 --- a/ui/viewgroup.cpp +++ b/ui/viewgroup.cpp @@ -1,4 +1,5 @@ #include "base/display.h" +#include "base/functional.h" #include "base/logging.h" #include "ui/ui_context.h" #include "ui/view.h" @@ -368,7 +369,18 @@ void FrameLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec ve } void FrameLayout::Layout() { + for (size_t i = 0; i < views_.size(); i++) { + float w = views_[i]->GetMeasuredWidth(); + float h = views_[i]->GetMeasuredHeight(); + Bounds bounds; + bounds.w = w; + bounds.h = h; + + bounds.x = bounds_.x + (measuredWidth_ - w) / 2; + bounds.y = bounds_.y + (measuredWidth_ - h) / 2; + views_[i]->SetBounds(bounds); + } } void ScrollView::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) { @@ -537,8 +549,8 @@ void AnchorLayout::Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec v if (params->top >= 0 && params->bottom >= 0) { height = measuredHeight_ - params->top - params->bottom; } - specW = MeasureSpec(EXACTLY, width); - specH = MeasureSpec(EXACTLY, height); + specW = width < 0 ? MeasureSpec(UNSPECIFIED) : MeasureSpec(EXACTLY, width); + specH = height < 0 ? MeasureSpec(UNSPECIFIED) : MeasureSpec(EXACTLY, height); } views_[i]->Measure(dc, specW, specH); @@ -562,12 +574,18 @@ void AnchorLayout::Layout() { bottom = params->bottom; } - if (left >= 0) vBounds.x = bounds_.x + left; - if (top >= 0) vBounds.y = bounds_.y + top; - if (right >= 0) vBounds.x = bounds_.x2() - right - vBounds.w; - if (bottom >= 0) vBounds.y = bounds_.y2() - bottom - vBounds.y; + if (left >= 0) + vBounds.x = bounds_.x + left; + else if (right >= 0) + vBounds.x = bounds_.x2() - right - vBounds.w; + + if (top >= 0) + vBounds.y = bounds_.y + top; + else if (bottom >= 0) + vBounds.y = bounds_.y2() - bottom - vBounds.h; views_[i]->SetBounds(vBounds); + views_[i]->Layout(); } } @@ -628,8 +646,7 @@ void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root) { } void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root) { - if (input_state.pad_buttons_down & (PAD_BUTTON_LEFT | PAD_BUTTON_RIGHT | PAD_BUTTON_UP | PAD_BUTTON_DOWN)) - { + if (input_state.pad_buttons_down & (PAD_BUTTON_LEFT | PAD_BUTTON_RIGHT | PAD_BUTTON_UP | PAD_BUTTON_DOWN)) { EnableFocusMovement(true); if (!GetFocusedView()) { root->SetFocus(); @@ -651,4 +668,31 @@ void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root) { root->Update(input_state); } +ListView::ListView(ListAdaptor *a, LayoutParams *layoutParams) + : ScrollView(ORIENT_VERTICAL, layoutParams), adaptor_(a) { + // 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++) { + View * v = Add(adaptor_->CreateItemView(i)); + adaptor_->AddEventCallback(v, std::bind(&ListView::OnItemCallback, this, i, placeholder::_1)); + } +} + +EventReturn ListView::OnItemCallback(int num, EventParams &e) { + EventParams ev; + ev.v = e.v; + ev.a = num; + OnChoice.Trigger(ev); + return EVENT_DONE; +} + +View *ChoiceListAdaptor::CreateItemView(int index) { + return new Choice(items_[index]); +} + +bool ChoiceListAdaptor::AddEventCallback(View *view, std::function callback) { + Choice *choice = (Choice *)view; + choice->OnClick.Add(callback); + return EVENT_DONE; +} + } // namespace UI \ No newline at end of file diff --git a/ui/viewgroup.h b/ui/viewgroup.h index 011e8a27ba..8bf8784730 100644 --- a/ui/viewgroup.h +++ b/ui/viewgroup.h @@ -51,6 +51,7 @@ protected: }; // A frame layout contains a single child view (normally). +// It simply centers the child view. class FrameLayout : public ViewGroup { public: void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert); @@ -65,7 +66,9 @@ enum { class AnchorLayoutParams : public LayoutParams { public: AnchorLayoutParams(Size w, Size h, float l, float t, float r, float b) - : LayoutParams(w, h), left(l), top(t), right(r), bottom(b) {} + : LayoutParams(w, h), left(l), top(t), right(r), bottom(b) { + + } AnchorLayoutParams(float l, float t, float r, float b) : LayoutParams(WRAP_CONTENT, WRAP_CONTENT), left(l), top(t), right(r), bottom(b) {} @@ -232,6 +235,40 @@ private: std::vector tabs_; }; +// Yes, this feels a bit Java-ish... +class ListAdaptor { +public: + virtual ~ListAdaptor() {} + virtual View *CreateItemView(int index) = 0; + virtual int GetNumItems() = 0; + virtual bool AddEventCallback(View *view, std::function callback) { return false; } +}; + +class ChoiceListAdaptor { +public: + ChoiceListAdaptor(const char *items[], int numItems) : items_(items), numItems_(numItems) {} + virtual View *CreateItemView(int index); + virtual int GetNumItems() { return numItems_; } + virtual bool AddEventCallback(View *view, std::function callback); + +private: + const char **items_; + int numItems_; +}; + +// A list view is a scroll view with autogenerated items. +// In the future, it might be smart and load/unload items as they go, but currently not. +class ListView : public ScrollView { +public: + ListView(ListAdaptor *a, LayoutParams *layoutParams); + + Event OnChoice; + +private: + EventReturn OnItemCallback(int num, EventParams &e); + ListAdaptor *adaptor_; +}; + void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root); void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root);