diff --git a/Android.mk b/Android.mk
index 2311998a6..9edfc363d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -63,6 +63,7 @@ LOCAL_SRC_FILES :=\
ui/ui_context.cpp \
ui/screen.cpp \
ui/virtual_input.cpp \
+ ui/drawing.cpp \
util/random/perlin.cpp \
util/text/utf8.cpp
diff --git a/input/gesture_detector.cpp b/input/gesture_detector.cpp
index 4a4e701e6..852b8fae6 100644
--- a/input/gesture_detector.cpp
+++ b/input/gesture_detector.cpp
@@ -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.
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.downTime = time_now_d();
p.downX = touch.x;
@@ -33,13 +33,18 @@ void GestureDetector::Update(const TouchInput &touch) {
if (touch.id == 0 && p.distanceY > p.distanceX) {
if (p.down) {
double timeDown = time_now_d() - p.downTime;
- if (p.distanceY * timeDown > 3) {
+ if (!active_ && p.distanceY * timeDown > 3) {
active_ |= GESTURE_DRAG_VERTICAL;
+ // Kill the drag
+ TouchInput inp2 = touch;
+ inp2.flags = TOUCH_UP;
+ return inp2;
}
} else {
active_ = 0;
}
}
+ return touch;
}
bool GestureDetector::IsGestureActive(Gesture gesture) const {
diff --git a/input/gesture_detector.h b/input/gesture_detector.h
index 8d0d2a2c9..4c8178fd4 100644
--- a/input/gesture_detector.h
+++ b/input/gesture_detector.h
@@ -1,4 +1,5 @@
#include "input/input_state.h"
+#include "math/geom2d.h"
// WIP - doesn't do much yet
// 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
// with the gesture you are interested in.
-class GestureDetector
-{
+class GestureDetector {
public:
GestureDetector();
- void Update(const TouchInput &touch);
+ TouchInput Update(const TouchInput &touch, const Bounds &bounds);
bool IsGestureActive(Gesture gesture) const;
void GetGestureInfo(Gesture gesture, float info[4]);
private:
+ Bounds bounds_;
+
// jazzhands!
enum Locals {
MAX_PTRS = 10
@@ -41,7 +43,6 @@ private:
};
Pointer pointers[MAX_PTRS];
- // ...
uint32_t active_;
};
diff --git a/math/geom2d.h b/math/geom2d.h
new file mode 100644
index 000000000..d165c80e4
--- /dev/null
+++ b/math/geom2d.h
@@ -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;
+};
+
+
diff --git a/native.vcxproj b/native.vcxproj
index 63827c8b7..0fc33aa6a 100644
--- a/native.vcxproj
+++ b/native.vcxproj
@@ -218,6 +218,7 @@
+
@@ -232,6 +233,7 @@
+
@@ -348,6 +350,7 @@
+
diff --git a/native.vcxproj.filters b/native.vcxproj.filters
index 86008ceba..ffe8acd25 100644
--- a/native.vcxproj.filters
+++ b/native.vcxproj.filters
@@ -260,6 +260,10 @@
ui
+
+ math
+
+
@@ -462,6 +466,7 @@
ui
+
diff --git a/ui/drawing.cpp b/ui/drawing.cpp
new file mode 100644
index 000000000..ddc9c5ac2
--- /dev/null
+++ b/ui/drawing.cpp
@@ -0,0 +1,3 @@
+namespace UI {
+
+}
\ No newline at end of file
diff --git a/ui/drawing.h b/ui/drawing.h
new file mode 100644
index 000000000..e69de29bb
diff --git a/ui/view.cpp b/ui/view.cpp
index 3fe8a30df..9e9f7c7ba 100644
--- a/ui/view.cpp
+++ b/ui/view.cpp
@@ -94,24 +94,37 @@ void Clickable::Click() {
};
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 (IsFocusMovementEnabled())
SetFocusedView(this);
- down_ = true;
+ downCountDown_ = 8;
} else {
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 (bounds_.Contains(input.x, input.y)) {
+ if (dragging_ && bounds_.Contains(input.x, input.y)) {
Click();
}
+ downCountDown_ = 0;
down_ = false;
+ dragging_ = false;
}
}
void Clickable::Update(const InputState &input_state) {
+ if (downCountDown_ == 1) {
+ downCountDown_ = 0;
+ dragging_ = true;
+ down_ = true;
+ } else if (downCountDown_ > 0) {
+ downCountDown_--;
+ }
if (!HasFocus())
return;
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) {
+ ClickableItem::Draw(dc);
int paddingX = 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);
}
@@ -137,16 +159,21 @@ void InfoItem::Draw(DrawContext &dc) {
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_.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) {
+ ClickableItem::Draw(dc);
int paddingX = 80;
int paddingY = 4;
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->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 {
diff --git a/ui/view.h b/ui/view.h
index 732b762a6..ba151b1c5 100644
--- a/ui/view.h
+++ b/ui/view.h
@@ -9,11 +9,14 @@
#include
#include
+#include
#include "base/mutex.h"
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "math/lin/matrix4x4.h"
+#include "math/math_util.h"
+#include "math/geom2d.h"
struct TouchInput;
struct InputState;
@@ -62,10 +65,14 @@ struct Theme {
int buttonSelected;
int checkOn;
int checkOff;
+ int whiteImage;
Style buttonStyle;
Style buttonFocusedStyle;
Style buttonDownStyle;
+
+ Style itemDownStyle;
+ Style itemFocusedStyle;
};
// The four cardinal directions should be enough, plus Prev/Next in "element order".
@@ -124,23 +131,6 @@ enum EventReturn {
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);
struct MeasureSpec {
@@ -200,6 +190,7 @@ private:
struct Margins {
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 horiz, uint8_t vert) : top(vert), bottom(vert), left(horiz), right(horiz) {}
uint8_t top;
uint8_t bottom;
uint8_t left;
@@ -217,7 +208,6 @@ public:
Size width;
Size height;
-private:
};
class LinearLayoutParams : public LayoutParams {
@@ -327,7 +317,7 @@ public:
class Clickable : public View {
public:
Clickable(LayoutParams *layoutParams)
- : View(layoutParams), down_(false) {}
+ : View(layoutParams), downCountDown_(0), down_(false), dragging_(false) {}
virtual void Touch(const TouchInput &input);
virtual void Update(const InputState &input_state);
@@ -340,6 +330,8 @@ protected:
// Use it for checking/unchecking checkboxes, etc.
virtual void Click();
+ int downCountDown_;
+ bool dragging_;
bool down_;
};
@@ -354,7 +346,6 @@ public:
private:
Style style_;
std::string text_;
- DISALLOW_COPY_AND_ASSIGN(Button);
};
class Item : public InertView {
@@ -381,10 +372,12 @@ public:
w = 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
// really is just a LinearLayout in a ScrollView, possibly with some special optimizations.
@@ -413,6 +406,18 @@ private:
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 {
public:
CheckBox(bool *toggle, const std::string &text, const std::string &smallText = "", LayoutParams *layoutParams = 0)
diff --git a/ui/viewgroup.cpp b/ui/viewgroup.cpp
index 197113583..8a882c70e 100644
--- a/ui/viewgroup.cpp
+++ b/ui/viewgroup.cpp
@@ -259,29 +259,44 @@ void FrameLayout::Layout() {
}
void ScrollView::Measure(const DrawContext &dc, MeasureSpec horiz, MeasureSpec vert) {
+ // Respect margins
+ Margins margins;
+ const LinearLayoutParams *params = dynamic_cast(views_[0]->GetLayoutParams());
+ if (params) {
+ margins = params->margins;
+ }
+
// The scroll view itself simply obeys its parent.
MeasureBySpec(layoutParams_->width, 0.0f, horiz, &measuredWidth_);
MeasureBySpec(layoutParams_->height, 0.0f, vert, &measuredHeight_);
if (orientation_ == ORIENT_HORIZONTAL) {
- views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), vert);
+ views_[0]->Measure(dc, MeasureSpec(UNSPECIFIED), vert - (margins.top + margins.bottom));
} else {
- views_[0]->Measure(dc, horiz, MeasureSpec(UNSPECIFIED));
+ views_[0]->Measure(dc, horiz - (margins.left + margins.right), MeasureSpec(UNSPECIFIED));
}
}
void ScrollView::Layout() {
Bounds scrolled;
- scrolled.w = views_[0]->GetMeasuredWidth();
- scrolled.h = views_[0]->GetMeasuredHeight();
+
+ // Respect margins
+ Margins margins;
+ const LinearLayoutParams *params = dynamic_cast(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_) {
case ORIENT_HORIZONTAL:
scrolled.x = bounds_.x - scrollPos_;
- scrolled.y = bounds_.y;
+ scrolled.y = bounds_.y + margins.top;
break;
case ORIENT_VERTICAL:
- scrolled.x = bounds_.x;
+ scrolled.x = bounds_.x + margins.left;
scrolled.y = bounds_.y - scrollPos_;
break;
}
@@ -293,17 +308,18 @@ void ScrollView::Touch(const TouchInput &input) {
if ((input.flags & TOUCH_DOWN) && input.id == 0) {
scrollStart_ = scrollPos_;
}
-
- gesture_.Update(input);
+
+ TouchInput input2 = gesture_.Update(input, bounds_);
if (gesture_.IsGestureActive(GESTURE_DRAG_VERTICAL)) {
float info[4];
gesture_.GetGestureInfo(GESTURE_DRAG_VERTICAL, info);
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.
if (scrollPos_ < 0.0f) {
scrollPos_ = 0.0f;