ppsspp/ui/viewgroup.h

332 lines
9.9 KiB
C
Raw Normal View History

2013-05-03 00:21:39 +02:00
#pragma once
2013-06-01 18:59:03 +02:00
#include "base/logging.h"
2013-05-03 00:21:39 +02:00
#include "ui/view.h"
#include "math/geom2d.h"
2013-05-03 00:21:39 +02:00
#include "input/gesture_detector.h"
namespace UI {
struct NeighborResult {
NeighborResult() : view(0), score(0) {}
NeighborResult(View *v, float s) : view(v), score(s) {}
View *view;
float score;
};
2013-05-03 00:21:39 +02:00
class ViewGroup : public View {
public:
2013-07-16 00:25:08 +02:00
ViewGroup(LayoutParams *layoutParams = 0) : View(layoutParams), hasDropShadow_(false) {}
2013-05-03 00:21:39 +02:00
virtual ~ViewGroup();
// Pass through external events to children.
2013-07-08 12:34:39 +02:00
virtual void Key(const KeyInput &input);
2013-05-03 00:21:39 +02:00
virtual void Touch(const TouchInput &input);
// By default, a container will layout to its own bounds.
2013-05-28 00:32:00 +02:00
virtual void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert) = 0;
2013-05-03 00:21:39 +02:00
virtual void Layout() = 0;
virtual void Update(const InputState &input_state);
2013-05-03 00:21:39 +02:00
2013-05-28 00:32:00 +02:00
virtual void Draw(UIContext &dc);
2013-05-03 00:21:39 +02:00
// These should be unused.
virtual float GetContentWidth() const { return 0.0f; }
virtual float GetContentHeight() const { return 0.0f; }
// Takes ownership! DO NOT add a view to multiple parents!
2013-06-01 18:59:03 +02:00
template <class T>
T *Add(T *view) { views_.push_back(view); return view; }
2013-05-03 00:21:39 +02:00
virtual bool SetFocus();
virtual bool SubviewFocused(View *view);
2013-05-03 00:21:39 +02:00
// Assumes that layout has taken place.
NeighborResult FindNeighbor(View *view, FocusDirection direction, NeighborResult best);
2013-05-03 00:21:39 +02:00
virtual bool CanBeFocused() const { return false; }
2013-06-09 13:01:36 +02:00
virtual bool IsViewGroup() const { return true; }
2013-07-16 00:25:08 +02:00
virtual void SetBG(const Drawable &bg) { bg_ = bg; }
virtual void Clear();
View *GetViewByIndex(int index) { return views_[index]; }
void SetHasDropShadow(bool has) { hasDropShadow_ = has; }
2013-05-03 00:21:39 +02:00
protected:
std::vector<View *> views_;
2013-07-16 00:25:08 +02:00
Drawable bg_;
bool hasDropShadow_;
2013-05-03 00:21:39 +02:00
};
// A frame layout contains a single child view (normally).
// It simply centers the child view.
2013-05-03 00:21:39 +02:00
class FrameLayout : public ViewGroup {
public:
2013-05-28 00:32:00 +02:00
void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
2013-05-03 00:21:39 +02:00
void Layout();
};
2013-05-27 22:22:35 +02:00
enum {
NONE = -1,
};
class AnchorLayoutParams : public LayoutParams {
2013-05-03 00:21:39 +02:00
public:
2013-05-27 22:22:35 +02:00
AnchorLayoutParams(Size w, Size h, float l, float t, float r, float b)
2013-06-09 13:01:36 +02:00
: LayoutParams(w, h, LP_ANCHOR), left(l), top(t), right(r), bottom(b) {
}
2013-05-27 22:22:35 +02:00
AnchorLayoutParams(float l, float t, float r, float b)
2013-06-09 13:01:36 +02:00
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT, LP_ANCHOR), left(l), top(t), right(r), bottom(b) {}
2013-05-27 22:22:35 +02:00
// These are not bounds, but distances from the container edges.
// Set to NONE to not attach this edge to the container.
float left, top, right, bottom;
};
class AnchorLayout : public ViewGroup {
public:
AnchorLayout(LayoutParams *layoutParams = 0) : ViewGroup(layoutParams) {}
2013-05-28 00:32:00 +02:00
void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
2013-05-03 00:21:39 +02:00
void Layout();
};
2013-05-27 22:22:35 +02:00
class LinearLayoutParams : public LayoutParams {
public:
LinearLayoutParams()
2013-06-09 13:01:36 +02:00
: LayoutParams(LP_LINEAR), weight(0.0f), gravity(G_TOPLEFT), hasMargins_(false) {}
2013-06-01 18:59:03 +02:00
explicit LinearLayoutParams(float wgt, Gravity grav = G_TOPLEFT)
2013-06-09 13:01:36 +02:00
: LayoutParams(LP_LINEAR), weight(wgt), gravity(grav), hasMargins_(false) {}
2013-06-01 18:59:03 +02:00
LinearLayoutParams(float wgt, const Margins &mgn)
2013-06-09 13:01:36 +02:00
: LayoutParams(LP_LINEAR), weight(wgt), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
2013-05-27 22:22:35 +02:00
LinearLayoutParams(Size w, Size h, float wgt = 0.0f, Gravity grav = G_TOPLEFT)
2013-06-09 13:01:36 +02:00
: LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(grav), hasMargins_(false) {}
2013-05-27 22:22:35 +02:00
LinearLayoutParams(Size w, Size h, float wgt, Gravity grav, const Margins &mgn)
2013-06-09 13:01:36 +02:00
: LayoutParams(w, h, LP_LINEAR), weight(wgt), gravity(grav), margins(mgn), hasMargins_(true) {}
LinearLayoutParams(Size w, Size h, const Margins &mgn)
2013-06-09 13:01:36 +02:00
: LayoutParams(w, h, LP_LINEAR), weight(0.0f), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
2013-05-27 22:22:35 +02:00
LinearLayoutParams(const Margins &mgn)
2013-06-09 13:01:36 +02:00
: LayoutParams(WRAP_CONTENT, WRAP_CONTENT, LP_LINEAR), weight(0.0f), gravity(G_TOPLEFT), margins(mgn), hasMargins_(true) {}
2013-05-27 22:22:35 +02:00
float weight;
Gravity gravity;
Margins margins;
bool HasMargins() const { return hasMargins_; }
private:
bool hasMargins_;
};
2013-05-03 00:21:39 +02:00
class LinearLayout : public ViewGroup {
public:
LinearLayout(Orientation orientation, LayoutParams *layoutParams = 0)
2013-06-04 22:05:17 +02:00
: ViewGroup(layoutParams), orientation_(orientation), defaultMargins_(0), spacing_(10) {}
2013-05-03 00:21:39 +02:00
2013-05-28 00:32:00 +02:00
void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
2013-05-03 00:21:39 +02:00
void Layout();
2013-07-17 01:03:29 +02:00
void SetSpacing(float spacing) {
spacing_ = spacing;
}
2013-05-03 00:21:39 +02:00
private:
Orientation orientation_;
Margins defaultMargins_;
float spacing_;
};
// GridLayout is a little different from the Android layout. This one has fixed size
// rows and columns. Items are not allowed to deviate from the set sizes.
// Initially, only horizontal layout is supported.
struct GridLayoutSettings {
GridLayoutSettings() : orientation(ORIENT_HORIZONTAL), columnWidth(100), rowHeight(50), spacing(5), fillCells(false) {}
GridLayoutSettings(int colW, int colH, int spac = 5) : orientation(ORIENT_HORIZONTAL), columnWidth(colW), rowHeight(colH), spacing(spac), fillCells(false) {}
Orientation orientation;
int columnWidth;
int rowHeight;
int spacing;
bool fillCells;
};
2013-05-03 00:21:39 +02:00
class GridLayout : public ViewGroup {
public:
GridLayout(GridLayoutSettings settings, LayoutParams *layoutParams = 0)
: ViewGroup(layoutParams), settings_(settings) {
if (settings.orientation != ORIENT_HORIZONTAL)
ELOG("GridLayout: Vertical layouts not yet supported");
}
2013-05-03 00:21:39 +02:00
2013-05-28 00:32:00 +02:00
void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
2013-05-03 00:21:39 +02:00
void Layout();
private:
GridLayoutSettings settings_;
2013-05-03 00:21:39 +02:00
};
// A scrollview usually contains just a single child - a linear layout or similar.
class ScrollView : public ViewGroup {
public:
2013-05-25 15:12:46 +02:00
ScrollView(Orientation orientation, LayoutParams *layoutParams = 0) :
2013-06-04 22:05:17 +02:00
ViewGroup(layoutParams),
orientation_(orientation),
scrollPos_(0),
scrollStart_(0),
scrollMax_(0),
scrollTarget_(0),
scrollToTarget_(false) {}
2013-05-03 00:21:39 +02:00
2013-05-28 00:32:00 +02:00
void Measure(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert);
2013-05-03 00:21:39 +02:00
void Layout();
2013-07-08 12:34:39 +02:00
void Key(const KeyInput &input);
2013-05-03 00:21:39 +02:00
void Touch(const TouchInput &input);
2013-05-28 00:32:00 +02:00
void Draw(UIContext &dc);
2013-05-03 00:21:39 +02:00
void ScrollTo(float newScrollPos);
2013-07-08 12:34:39 +02:00
void ScrollRelative(float distance);
2013-05-28 00:50:19 +02:00
void Update(const InputState &input_state);
// Override so that we can scroll to the active one after moving the focus.
virtual bool SubviewFocused(View *view);
2013-05-03 00:21:39 +02:00
private:
void ClampScrollPos(float &pos);
2013-07-08 12:34:39 +02:00
2013-05-03 00:21:39 +02:00
GestureDetector gesture_;
Orientation orientation_;
float scrollPos_;
float scrollStart_;
float scrollMax_;
2013-05-28 00:50:19 +02:00
float scrollTarget_;
bool scrollToTarget_;
2013-05-03 00:21:39 +02:00
};
class ViewPager : public ScrollView {
public:
};
class ChoiceStrip : public LinearLayout {
public:
ChoiceStrip(Orientation orientation, LayoutParams *layoutParams = 0)
2013-07-17 01:03:29 +02:00
: LinearLayout(orientation, layoutParams), selected_(0) { SetSpacing(0.0f); }
void AddChoice(const std::string &title);
int GetSelection() const { return selected_; }
void SetSelection(int sel);
Event OnChoice;
private:
EventReturn OnChoiceClick(EventParams &e);
int selected_;
};
class TabHolder : public LinearLayout {
public:
TabHolder(Orientation orientation, float stripSize, LayoutParams *layoutParams = 0)
: LinearLayout(Opposite(orientation), layoutParams),
2013-07-14 13:51:30 +02:00
orientation_(orientation), stripSize_(stripSize), currentTab_(0) {
tabStrip_ = new ChoiceStrip(orientation, new LinearLayoutParams(stripSize, WRAP_CONTENT));
Add(tabStrip_);
tabStrip_->OnChoice.Handle(this, &TabHolder::OnTabClick);
}
template <class T>
T *AddTab(const std::string &title, T *tabContents) {
tabContents->ReplaceLayoutParams(new LinearLayoutParams(1.0f));
tabs_.push_back(tabContents);
tabStrip_->AddChoice(title);
Add(tabContents);
if (tabs_.size() > 1)
tabContents->SetVisibility(V_GONE);
return tabContents;
}
void SetCurrentTab(int tab) {
tabs_[currentTab_]->SetVisibility(V_GONE);
currentTab_ = tab;
tabs_[currentTab_]->SetVisibility(V_VISIBLE);
}
private:
EventReturn OnTabClick(EventParams &e);
ChoiceStrip *tabStrip_;
Orientation orientation_;
float stripSize_;
2013-06-11 20:33:41 +02:00
int currentTab_;
std::vector<View *> 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<EventReturn(EventParams&)> callback) { return false; }
2013-07-16 00:25:08 +02:00
virtual std::string GetTitle(int index) { return ""; }
virtual void SetSelected(int sel) { }
virtual int GetSelected() { return -1; }
};
2013-07-16 00:25:08 +02:00
class ChoiceListAdaptor : public ListAdaptor {
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<EventReturn(EventParams&)> callback);
private:
const char **items_;
int numItems_;
};
2013-07-16 00:25:08 +02:00
// The "selected" item is what was previously selected (optional). This items will be drawn differently.
class StringVectorListAdaptor : public ListAdaptor {
public:
StringVectorListAdaptor() : selected_(-1) {}
StringVectorListAdaptor(const std::vector<std::string> &items, int selected = -1) : items_(items), selected_(selected) {}
virtual View *CreateItemView(int index);
virtual int GetNumItems() { return (int)items_.size(); }
virtual bool AddEventCallback(View *view, std::function<EventReturn(EventParams&)> callback);
void SetSelected(int sel) { selected_ = sel; }
virtual std::string GetTitle(int index) { return items_[index]; }
virtual int GetSelected() { return selected_; }
private:
std::vector<std::string> items_;
int selected_;
};
// 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:
2013-07-16 00:25:08 +02:00
ListView(ListAdaptor *a, LayoutParams *layoutParams = 0);
2013-07-16 00:25:08 +02:00
int GetSelected() { return adaptor_->GetSelected(); }
Event OnChoice;
private:
2013-07-16 00:25:08 +02:00
void CreateAllItems();
EventReturn OnItemCallback(int num, EventParams &e);
ListAdaptor *adaptor_;
2013-07-16 00:25:08 +02:00
LinearLayout *linLayout_;
};
2013-05-28 00:32:00 +02:00
void LayoutViewHierarchy(const UIContext &dc, ViewGroup *root);
void UpdateViewHierarchy(const InputState &input_state, ViewGroup *root);
2013-05-03 00:21:39 +02:00
2013-07-14 13:51:30 +02:00
} // namespace UI