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;