More new UI work

This commit is contained in:
Henrik Rydgard 2013-05-25 16:52:27 +02:00
parent 02a3cc1b5b
commit 7bad683cae
11 changed files with 154 additions and 46 deletions

View File

@ -63,6 +63,7 @@ LOCAL_SRC_FILES :=\
ui/ui_context.cpp \ ui/ui_context.cpp \
ui/screen.cpp \ ui/screen.cpp \
ui/virtual_input.cpp \ ui/virtual_input.cpp \
ui/drawing.cpp \
util/random/perlin.cpp \ util/random/perlin.cpp \
util/text/utf8.cpp util/text/utf8.cpp

View File

@ -9,10 +9,10 @@ GestureDetector::GestureDetector() : active_(0) {
} }
void GestureDetector::Update(const TouchInput &touch) { TouchInput GestureDetector::Update(const TouchInput &touch, const Bounds &bounds) {
// Mouse / 1-finger-touch control. // Mouse / 1-finger-touch control.
Pointer &p = pointers[touch.id]; Pointer &p = pointers[touch.id];
if (touch.flags & TOUCH_DOWN) { if ((touch.flags & TOUCH_DOWN) && bounds.Contains(touch.x, touch.y)) {
p.down = true; p.down = true;
p.downTime = time_now_d(); p.downTime = time_now_d();
p.downX = touch.x; p.downX = touch.x;
@ -33,13 +33,18 @@ void GestureDetector::Update(const TouchInput &touch) {
if (touch.id == 0 && p.distanceY > p.distanceX) { if (touch.id == 0 && p.distanceY > p.distanceX) {
if (p.down) { if (p.down) {
double timeDown = time_now_d() - p.downTime; double timeDown = time_now_d() - p.downTime;
if (p.distanceY * timeDown > 3) { if (!active_ && p.distanceY * timeDown > 3) {
active_ |= GESTURE_DRAG_VERTICAL; active_ |= GESTURE_DRAG_VERTICAL;
// Kill the drag
TouchInput inp2 = touch;
inp2.flags = TOUCH_UP;
return inp2;
} }
} else { } else {
active_ = 0; active_ = 0;
} }
} }
return touch;
} }
bool GestureDetector::IsGestureActive(Gesture gesture) const { bool GestureDetector::IsGestureActive(Gesture gesture) const {

View File

@ -1,4 +1,5 @@
#include "input/input_state.h" #include "input/input_state.h"
#include "math/geom2d.h"
// WIP - doesn't do much yet // WIP - doesn't do much yet
// Mainly for detecting (multi-)touch gestures but also useable for left button mouse dragging etc. // Mainly for detecting (multi-)touch gestures but also useable for left button mouse dragging etc.
@ -13,15 +14,16 @@ enum Gesture {
// May track multiple gestures at the same time. You simply call GetGestureInfo // May track multiple gestures at the same time. You simply call GetGestureInfo
// with the gesture you are interested in. // with the gesture you are interested in.
class GestureDetector class GestureDetector {
{
public: public:
GestureDetector(); GestureDetector();
void Update(const TouchInput &touch); TouchInput Update(const TouchInput &touch, const Bounds &bounds);
bool IsGestureActive(Gesture gesture) const; bool IsGestureActive(Gesture gesture) const;
void GetGestureInfo(Gesture gesture, float info[4]); void GetGestureInfo(Gesture gesture, float info[4]);
private: private:
Bounds bounds_;
// jazzhands! // jazzhands!
enum Locals { enum Locals {
MAX_PTRS = 10 MAX_PTRS = 10
@ -41,7 +43,6 @@ private:
}; };
Pointer pointers[MAX_PTRS]; Pointer pointers[MAX_PTRS];
// ...
uint32_t active_; uint32_t active_;
}; };

42
math/geom2d.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
struct Point {
Point() {}
Point(float x_, float y_) : x(x_), y(y_) {}
float x;
float y;
float distanceTo(const Point &other) const {
float dx = other.x - x, dy = other.y - y;
return sqrtf(dx*dx + dy*dy);
}
/*
FocusDirection directionTo(const Point &other) const {
int angle = atan2f(other.y - y, other.x - x) / (2 * M_PI) - 0.125;
}*/
};
// Resolved bounds on screen after layout.
struct Bounds {
bool Contains(float px, float py) const {
return (px >= x && py >= y && px < x + w && py < y + h);
}
float x2() const { return x + w; }
float y2() const { return y + h; }
float centerX() const { return x + w * 0.5f; }
float centerY() const { return y + h * 0.5f; }
Point Center() const {
return Point(centerX(), centerY());
}
float x;
float y;
float w;
float h;
};

View File

@ -218,6 +218,7 @@
<ClInclude Include="json\json_writer.h" /> <ClInclude Include="json\json_writer.h" />
<ClInclude Include="math\compression.h" /> <ClInclude Include="math\compression.h" />
<ClInclude Include="math\curves.h" /> <ClInclude Include="math\curves.h" />
<ClInclude Include="math\geom2d.h" />
<ClInclude Include="math\lin\aabb.h" /> <ClInclude Include="math\lin\aabb.h" />
<ClInclude Include="math\lin\matrix4x4.h" /> <ClInclude Include="math\lin\matrix4x4.h" />
<ClInclude Include="math\lin\plane.h" /> <ClInclude Include="math\lin\plane.h" />
@ -232,6 +233,7 @@
<ClInclude Include="thread\thread.h" /> <ClInclude Include="thread\thread.h" />
<ClInclude Include="thread\threadpool.h" /> <ClInclude Include="thread\threadpool.h" />
<ClInclude Include="thread\threadutil.h" /> <ClInclude Include="thread\threadutil.h" />
<ClInclude Include="ui\drawing.h" />
<ClInclude Include="ui\screen.h" /> <ClInclude Include="ui\screen.h" />
<ClInclude Include="ui\ui.h" /> <ClInclude Include="ui\ui.h" />
<ClInclude Include="ui\ui_context.h" /> <ClInclude Include="ui\ui_context.h" />
@ -348,6 +350,7 @@
<ClCompile Include="thread\prioritizedworkqueue.cpp" /> <ClCompile Include="thread\prioritizedworkqueue.cpp" />
<ClCompile Include="thread\threadpool.cpp" /> <ClCompile Include="thread\threadpool.cpp" />
<ClCompile Include="thread\threadutil.cpp" /> <ClCompile Include="thread\threadutil.cpp" />
<ClCompile Include="ui\drawing.cpp" />
<ClCompile Include="ui\screen.cpp" /> <ClCompile Include="ui\screen.cpp" />
<ClCompile Include="ui\ui.cpp" /> <ClCompile Include="ui\ui.cpp" />
<ClCompile Include="ui\ui_context.cpp" /> <ClCompile Include="ui\ui_context.cpp" />

View File

@ -260,6 +260,10 @@
<ClInclude Include="ui\viewgroup.h"> <ClInclude Include="ui\viewgroup.h">
<Filter>ui</Filter> <Filter>ui</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="math\geom2d.h">
<Filter>math</Filter>
</ClInclude>
<ClInclude Include="ui\drawing.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="gfx\gl_debug_log.cpp"> <ClCompile Include="gfx\gl_debug_log.cpp">
@ -462,6 +466,7 @@
<ClCompile Include="ui\viewgroup.cpp"> <ClCompile Include="ui\viewgroup.cpp">
<Filter>ui</Filter> <Filter>ui</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="ui\drawing.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="gfx"> <Filter Include="gfx">

3
ui/drawing.cpp Normal file
View File

@ -0,0 +1,3 @@
namespace UI {
}

0
ui/drawing.h Normal file
View File

View File

@ -94,24 +94,37 @@ void Clickable::Click() {
}; };
void Clickable::Touch(const TouchInput &input) { void Clickable::Touch(const TouchInput &input) {
if (input.flags & (TOUCH_DOWN | TOUCH_MOVE)) { if (input.flags & TOUCH_DOWN) {
if (bounds_.Contains(input.x, input.y)) { if (bounds_.Contains(input.x, input.y)) {
if (IsFocusMovementEnabled()) if (IsFocusMovementEnabled())
SetFocusedView(this); SetFocusedView(this);
down_ = true; downCountDown_ = 8;
} else { } else {
down_ = false; down_ = false;
dragging_ = false;
} }
} else if (input.flags & TOUCH_MOVE) {
if (dragging_)
down_ = bounds_.Contains(input.x, input.y);
} }
if (input.flags & TOUCH_UP) { if (input.flags & TOUCH_UP) {
if (bounds_.Contains(input.x, input.y)) { if (dragging_ && bounds_.Contains(input.x, input.y)) {
Click(); Click();
} }
downCountDown_ = 0;
down_ = false; down_ = false;
dragging_ = false;
} }
} }
void Clickable::Update(const InputState &input_state) { void Clickable::Update(const InputState &input_state) {
if (downCountDown_ == 1) {
downCountDown_ = 0;
dragging_ = true;
down_ = true;
} else if (downCountDown_ > 0) {
downCountDown_--;
}
if (!HasFocus()) if (!HasFocus())
return; return;
if (input_state.pad_buttons_down & PAD_BUTTON_A) { if (input_state.pad_buttons_down & PAD_BUTTON_A) {
@ -125,10 +138,19 @@ void Clickable::Update(const InputState &input_state) {
} }
} }
void ClickableItem::Draw(DrawContext &dc) {
if (down_) {
dc.draw->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.bgColor);
} else if (HasFocus()) {
dc.draw->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y2(), dc.theme->itemFocusedStyle.bgColor);
}
}
void Choice::Draw(DrawContext &dc) { void Choice::Draw(DrawContext &dc) {
ClickableItem::Draw(dc);
int paddingX = 4; int paddingX = 4;
int paddingY = 4; int paddingY = 4;
dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), paddingX, paddingY, 0xFFFFFFFF, ALIGN_TOPLEFT); dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
// dc.draw->DrawText(dc.theme->uiFontSmaller, text_.c_str(), paddingX, paddingY, 0xFFFFFFFF, ALIGN_TOPLEFT); // dc.draw->DrawText(dc.theme->uiFontSmaller, text_.c_str(), paddingX, paddingY, 0xFFFFFFFF, ALIGN_TOPLEFT);
} }
@ -137,16 +159,21 @@ void InfoItem::Draw(DrawContext &dc) {
int paddingY = 4; int paddingY = 4;
dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER); dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x2() - paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER | ALIGN_RIGHT); dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x2() - paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER | ALIGN_RIGHT);
dc.draw->hLine(bounds_.x, bounds_.y, bounds_.x2(), 0xFFFFFFFF); dc.draw->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y, bounds_.x2(), bounds_.y + 2, dc.theme->itemDownStyle.bgColor);
}
void ItemHeader::Draw(DrawContext &dc) {
dc.draw->DrawText(dc.theme->uiFontSmaller, text_.c_str(), bounds_.x + 4, bounds_.y, 0xFF707070, ALIGN_LEFT);
dc.draw->DrawImageStretch(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->itemDownStyle.bgColor);
} }
void CheckBox::Draw(DrawContext &dc) { void CheckBox::Draw(DrawContext &dc) {
ClickableItem::Draw(dc);
int paddingX = 80; int paddingX = 80;
int paddingY = 4; int paddingY = 4;
dc.draw->DrawImage(dc.theme->checkOn, bounds_.x + 30, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER); dc.draw->DrawImage(dc.theme->checkOn, bounds_.x + 30, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER); dc.draw->DrawText(dc.theme->uiFont, text_.c_str(), bounds_.x + paddingX, bounds_.centerY(), 0xFFFFFFFF, ALIGN_VCENTER);
// dc.draw->DrawText(dc.theme->uiFontSmaller, text_.c_str(), paddingX, paddingY, 0xFFFFFFFF, ALIGN_TOPLEFT); // dc.draw->DrawText(dc.theme->uiFontSmaller, text_.c_str(), paddingX, paddingY, 0xFFFFFFFF, ALIGN_TOPLEFT);
dc.draw->hLine(bounds_.x, bounds_.y, bounds_.x2(), 0xFFFFFFFF);
} }
void Button::GetContentDimensions(const DrawContext &dc, float &w, float &h) const { void Button::GetContentDimensions(const DrawContext &dc, float &w, float &h) const {

View File

@ -9,11 +9,14 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <cmath>
#include "base/mutex.h" #include "base/mutex.h"
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/scoped_ptr.h" #include "base/scoped_ptr.h"
#include "math/lin/matrix4x4.h" #include "math/lin/matrix4x4.h"
#include "math/math_util.h"
#include "math/geom2d.h"
struct TouchInput; struct TouchInput;
struct InputState; struct InputState;
@ -62,10 +65,14 @@ struct Theme {
int buttonSelected; int buttonSelected;
int checkOn; int checkOn;
int checkOff; int checkOff;
int whiteImage;
Style buttonStyle; Style buttonStyle;
Style buttonFocusedStyle; Style buttonFocusedStyle;
Style buttonDownStyle; Style buttonDownStyle;
Style itemDownStyle;
Style itemFocusedStyle;
}; };
// The four cardinal directions should be enough, plus Prev/Next in "element order". // The four cardinal directions should be enough, plus Prev/Next in "element order".
@ -124,23 +131,6 @@ enum EventReturn {
class ViewGroup; class ViewGroup;
// Resolved bounds on screen after layout.
struct Bounds {
bool Contains(float px, float py) const {
return (px >= x && py >= y && px < x + w && py < y + h);
}
float x2() const { return x + w; }
float y2() const { return y + h; }
float centerX() const { return x + w * 0.5f; }
float centerY() const { return y + h * 0.5f; }
float x;
float y;
float w;
float h;
};
void Fill(DrawContext &dc, const Bounds &bounds, const Drawable &drawable); void Fill(DrawContext &dc, const Bounds &bounds, const Drawable &drawable);
struct MeasureSpec { struct MeasureSpec {
@ -200,6 +190,7 @@ private:
struct Margins { struct Margins {
Margins() : top(0), bottom(0), left(0), right(0) {} Margins() : top(0), bottom(0), left(0), right(0) {}
explicit Margins(uint8_t all) : top(all), bottom(all), left(all), right(all) {} explicit Margins(uint8_t all) : top(all), bottom(all), left(all), right(all) {}
explicit Margins(uint8_t horiz, uint8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
uint8_t top; uint8_t top;
uint8_t bottom; uint8_t bottom;
uint8_t left; uint8_t left;
@ -217,7 +208,6 @@ public:
Size width; Size width;
Size height; Size height;
private:
}; };
class LinearLayoutParams : public LayoutParams { class LinearLayoutParams : public LayoutParams {
@ -327,7 +317,7 @@ public:
class Clickable : public View { class Clickable : public View {
public: public:
Clickable(LayoutParams *layoutParams) Clickable(LayoutParams *layoutParams)
: View(layoutParams), down_(false) {} : View(layoutParams), downCountDown_(0), down_(false), dragging_(false) {}
virtual void Touch(const TouchInput &input); virtual void Touch(const TouchInput &input);
virtual void Update(const InputState &input_state); virtual void Update(const InputState &input_state);
@ -340,6 +330,8 @@ protected:
// Use it for checking/unchecking checkboxes, etc. // Use it for checking/unchecking checkboxes, etc.
virtual void Click(); virtual void Click();
int downCountDown_;
bool dragging_;
bool down_; bool down_;
}; };
@ -354,7 +346,6 @@ public:
private: private:
Style style_; Style style_;
std::string text_; std::string text_;
DISALLOW_COPY_AND_ASSIGN(Button);
}; };
class Item : public InertView { class Item : public InertView {
@ -381,10 +372,12 @@ public:
w = 0.0f; w = 0.0f;
h = 0.0f; h = 0.0f;
} }
// Draws the item background.
virtual void Draw(DrawContext &dc);
}; };
// The following classes are mostly suitable as items in ListView which // The following classes are mostly suitable as items in ListView which
// really is just a LinearLayout in a ScrollView, possibly with some special optimizations. // really is just a LinearLayout in a ScrollView, possibly with some special optimizations.
@ -413,6 +406,18 @@ private:
std::string rightText_; std::string rightText_;
}; };
class ItemHeader : public Item {
public:
ItemHeader(const std::string &text, LayoutParams *layoutParams = 0)
: Item(layoutParams), text_(text) {
layoutParams_->width = FILL_PARENT;
layoutParams_->height = 26;
}
virtual void Draw(DrawContext &dc);
private:
std::string text_;
};
class CheckBox : public ClickableItem { class CheckBox : public ClickableItem {
public: public:
CheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0) CheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0)

View File

@ -259,29 +259,44 @@ void FrameLayout::Layout() {
} }
void ScrollView::Measure(const DrawContext &dc, MeasureSpec horiz, MeasureSpec vert) { void ScrollView::Measure(const DrawContext &dc, MeasureSpec horiz, MeasureSpec vert) {
// Respect margins
Margins margins;
const LinearLayoutParams *params = dynamic_cast<const LinearLayoutParams*>(views_[0]->GetLayoutParams());
if (params) {
margins = params->margins;
}
// The scroll view itself simply obeys its parent. // The scroll view itself simply obeys its parent.
MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_); MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_);
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_); MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
if (orientation_ == ORIENT_HORIZONTAL) { if (orientation_ == ORIENT_HORIZONTAL) {
views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), vert); views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), vert - (margins.top + margins.bottom));
} else { } else {
views_[0]->Measure(dc, horiz, MeasureSpec(UNSPECIFIED)); views_[0]->Measure(dc, horiz - (margins.left + margins.right), MeasureSpec(UNSPECIFIED));
} }
} }
void ScrollView::Layout() { void ScrollView::Layout() {
Bounds scrolled; Bounds scrolled;
scrolled.w = views_[0]->GetMeasuredWidth();
scrolled.h = views_[0]->GetMeasuredHeight(); // Respect margins
Margins margins;
const LinearLayoutParams *params = dynamic_cast<const LinearLayoutParams*>(views_[0]->GetLayoutParams());
if (params) {
margins = params->margins;
}
scrolled.w = views_[0]->GetMeasuredWidth() - (margins.left + margins.right);
scrolled.h = views_[0]->GetMeasuredHeight() - (margins.top + margins.bottom);
switch (orientation_) { switch (orientation_) {
case ORIENT_HORIZONTAL: case ORIENT_HORIZONTAL:
scrolled.x = bounds_.x - scrollPos_; scrolled.x = bounds_.x - scrollPos_;
scrolled.y = bounds_.y; scrolled.y = bounds_.y + margins.top;
break; break;
case ORIENT_VERTICAL: case ORIENT_VERTICAL:
scrolled.x = bounds_.x; scrolled.x = bounds_.x + margins.left;
scrolled.y = bounds_.y - scrollPos_; scrolled.y = bounds_.y - scrollPos_;
break; break;
} }
@ -293,17 +308,18 @@ void ScrollView::Touch(const TouchInput &input) {
if ((input.flags & TOUCH_DOWN) && input.id == 0) { if ((input.flags & TOUCH_DOWN) && input.id == 0) {
scrollStart_ = scrollPos_; scrollStart_ = scrollPos_;
} }
gesture_.Update(input); TouchInput input2 = gesture_.Update(input, bounds_);
if (gesture_.IsGestureActive(GESTURE_DRAG_VERTICAL)) { if (gesture_.IsGestureActive(GESTURE_DRAG_VERTICAL)) {
float info[4]; float info[4];
gesture_.GetGestureInfo(GESTURE_DRAG_VERTICAL, info); gesture_.GetGestureInfo(GESTURE_DRAG_VERTICAL, info);
scrollPos_ = scrollStart_ - info[0]; scrollPos_ = scrollStart_ - info[0];
} else {
ViewGroup::Touch(input);
} }
if (!(input.flags & TOUCH_DOWN) || bounds_.Contains(input.x, input.y))
ViewGroup::Touch(input2);
// Clamp scrollPos_. TODO: flinging, bouncing, etc. // Clamp scrollPos_. TODO: flinging, bouncing, etc.
if (scrollPos_ < 0.0f) { if (scrollPos_ < 0.0f) {
scrollPos_ = 0.0f; scrollPos_ = 0.0f;