Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / UI / TouchControlLayoutScreen.cpp
blobb6f2cd17f95bb8ac31a76abb0e6fc2c57ef18b7b
1 // Copyright (c) 2013- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include <vector>
20 #include "base/colorutil.h"
21 #include "gfx_es2/draw_buffer.h"
22 #include "i18n/i18n.h"
23 #include "ui/ui_context.h"
24 #include "ui_atlas.h"
26 #include "TouchControlLayoutScreen.h"
27 #include "TouchControlVisibilityScreen.h"
28 #include "Core/Config.h"
29 #include "Core/System.h"
30 #include "GamepadEmu.h"
32 static const int leftColumnWidth = 140;
34 // Ugly hackery, need to rework some stuff to get around this
35 static float local_dp_xres;
36 static float local_dp_yres;
38 static u32 GetButtonColor() {
39 return g_Config.iTouchButtonStyle == 1 ? 0xFFFFFF : 0xc0b080;
42 class DragDropButton : public MultiTouchButton {
43 public:
44 DragDropButton(float &x, float &y, int bgImg, int img, float &scale)
45 : MultiTouchButton(bgImg, img, scale, new UI::AnchorLayoutParams(fromFullscreenCoord(x), y*local_dp_yres, UI::NONE, UI::NONE, true)),
46 x_(x), y_(y), theScale_(scale) {
47 scale_ = theScale_;
50 virtual bool IsDown() {
51 // Don't want the button to enlarge and throw the user's perspective
52 // of button size off whack.
53 return false;
56 virtual void SavePosition() {
57 x_ = toFullscreenCoord(bounds_.centerX());
58 y_ = bounds_.centerY() / local_dp_yres;
59 scale_ = theScale_;
62 virtual float GetScale() const { return theScale_; }
63 virtual void SetScale(float s) { theScale_ = s; scale_ = s; }
65 virtual float GetSpacing() const { return 1.0f; }
66 virtual void SetSpacing(float s) { }
68 private:
69 // convert from screen coordinates (leftColumnWidth to dp_xres) to actual fullscreen coordinates (0 to 1.0)
70 inline float toFullscreenCoord(int screenx) {
71 return (float)(screenx - leftColumnWidth) / (local_dp_xres - leftColumnWidth);
74 // convert from external fullscreen coordinates(0 to 1.0) to the current partial coordinates (leftColumnWidth to dp_xres)
75 inline int fromFullscreenCoord(float controllerX) {
76 return leftColumnWidth + (local_dp_xres - leftColumnWidth) * controllerX;
79 float &x_, &y_;
80 float &theScale_;
83 class PSPActionButtons : public DragDropButton {
84 public:
85 PSPActionButtons(float &x, float &y, float &scale, float &spacing)
86 : DragDropButton(x, y, -1, -1, scale), spacing_(spacing) {
87 using namespace UI;
88 roundId_ = g_Config.iTouchButtonStyle ? I_ROUND_LINE : I_ROUND;
90 circleId_ = I_CIRCLE;
91 crossId_ = I_CROSS;
92 triangleId_ = I_TRIANGLE;
93 squareId_ = I_SQUARE;
95 circleVisible_ = triangleVisible_ = squareVisible_ = crossVisible_ = true;
98 void setCircleVisibility(bool visible){
99 circleVisible_ = visible;
102 void setCrossVisibility(bool visible){
103 crossVisible_ = visible;
106 void setTriangleVisibility(bool visible){
107 triangleVisible_ = visible;
110 void setSquareVisibility(bool visible){
111 squareVisible_ = visible;
114 void Draw(UIContext &dc) {
115 float opacity = g_Config.iTouchButtonOpacity / 100.0f;
117 uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
118 uint32_t color = colorAlpha(0xFFFFFF, opacity);
120 int centerX = bounds_.centerX();
121 int centerY = bounds_.centerY();
123 float spacing = spacing_ * baseActionButtonSpacing;
124 if (circleVisible_) {
125 dc.Draw()->DrawImageRotated(roundId_, centerX + spacing, centerY, scale_, 0, colorBg, false);
126 dc.Draw()->DrawImageRotated(circleId_, centerX + spacing, centerY, scale_, 0, color, false);
129 if (crossVisible_) {
130 dc.Draw()->DrawImageRotated(roundId_, centerX, centerY + spacing, scale_, 0, colorBg, false);
131 dc.Draw()->DrawImageRotated(crossId_, centerX, centerY + spacing, scale_, 0, color, false);
134 if (triangleVisible_) {
135 float y = centerY - spacing;
136 y -= 2.8f * scale_;
137 dc.Draw()->DrawImageRotated(roundId_, centerX, centerY - spacing, scale_, 0, colorBg, false);
138 dc.Draw()->DrawImageRotated(triangleId_, centerX, y, scale_, 0, color, false);
141 if (squareVisible_) {
142 dc.Draw()->DrawImageRotated(roundId_, centerX - spacing, centerY, scale_, 0, colorBg, false);
143 dc.Draw()->DrawImageRotated(squareId_, centerX - spacing, centerY, scale_, 0, color, false);
147 void GetContentDimensions(const UIContext &dc, float &w, float &h) const{
148 const AtlasImage &image = dc.Draw()->GetAtlas()->images[roundId_];
150 w = (2 * baseActionButtonSpacing * spacing_) + image.w * scale_;
151 h = (2 * baseActionButtonSpacing * spacing_) + image.h * scale_;
154 virtual float GetSpacing() const { return spacing_; }
155 virtual void SetSpacing(float s) { spacing_ = s; }
157 virtual void SavePosition() {
158 DragDropButton::SavePosition();
161 private:
162 bool circleVisible_, crossVisible_, triangleVisible_, squareVisible_;
164 int roundId_;
165 int circleId_, crossId_, triangleId_, squareId_;
167 float &spacing_;
170 class PSPDPadButtons : public DragDropButton {
171 public:
172 PSPDPadButtons(float &x, float &y, float &scale, float &spacing)
173 : DragDropButton(x, y, -1, -1, scale), spacing_(spacing) {
176 void Draw(UIContext &dc) {
177 float opacity = g_Config.iTouchButtonOpacity / 100.0f;
179 uint32_t colorBg = colorAlpha(GetButtonColor(), opacity);
180 uint32_t color = colorAlpha(0xFFFFFF, opacity);
182 static const float xoff[4] = {1, 0, -1, 0};
183 static const float yoff[4] = {0, 1, 0, -1};
185 int dirImage = g_Config.iTouchButtonStyle ? I_DIR_LINE : I_DIR;
187 for (int i = 0; i < 4; i++) {
188 float r = D_pad_Radius * spacing_;
189 float x = bounds_.centerX() + xoff[i] * r;
190 float y = bounds_.centerY() + yoff[i] * r;
191 float x2 = bounds_.centerX() + xoff[i] * (r + 10.f * scale_);
192 float y2 = bounds_.centerY() + yoff[i] * (r + 10.f * scale_);
193 float angle = i * M_PI / 2;
195 dc.Draw()->DrawImageRotated(dirImage, x, y, scale_, angle + PI, colorBg, false);
196 dc.Draw()->DrawImageRotated(I_ARROW, x2, y2, scale_, angle + PI, color);
200 void GetContentDimensions(const UIContext &dc, float &w, float &h) const{
201 const AtlasImage &image = dc.Draw()->GetAtlas()->images[I_DIR];
202 w = 2 * D_pad_Radius * spacing_ + image.w * scale_;
203 h = 2 * D_pad_Radius * spacing_ + image.h * scale_;
206 float GetSpacing() const { return spacing_; }
207 virtual void SetSpacing(float s) { spacing_ = s; }
209 private:
210 float &spacing_;
213 TouchControlLayoutScreen::TouchControlLayoutScreen() {
214 pickedControl_ = 0;
217 bool TouchControlLayoutScreen::touch(const TouchInput &touch) {
218 UIScreen::touch(touch);
220 using namespace UI;
222 int mode = mode_->GetSelection();
224 const Bounds &screen_bounds = screenManager()->getUIContext()->GetBounds();
226 if ((touch.flags & TOUCH_MOVE) && pickedControl_ != 0) {
227 if (mode == 0) {
228 const Bounds &bounds = pickedControl_->GetBounds();
230 int mintouchX = leftColumnWidth + bounds.w * 0.5;
231 int maxTouchX = screen_bounds.w - bounds.w * 0.5;
233 int minTouchY = bounds.h * 0.5;
234 int maxTouchY = screen_bounds.h - bounds.h * 0.5;
236 int newX = bounds.centerX(), newY = bounds.centerY();
238 // we have to handle x and y separately since even if x is blocked, y may not be.
239 if (touch.x > mintouchX && touch.x < maxTouchX) {
240 // if the leftmost point of the control is ahead of the margin,
241 // move it. Otherwise, don't.
242 newX = touch.x;
244 if (touch.y > minTouchY && touch.y < maxTouchY) {
245 newY = touch.y;
247 pickedControl_->ReplaceLayoutParams(new UI::AnchorLayoutParams(newX, newY, NONE, NONE, true));
248 } else if (mode == 1) {
249 // Resize. Vertical = scaling, horizontal = spacing;
250 // Up should be bigger so let's negate in that direction
251 float diffX = (touch.x - startX_);
252 float diffY = -(touch.y - startY_);
254 float movementScale = 0.02f;
255 float newScale = startScale_ + diffY * movementScale;
256 float newSpacing = startSpacing_ + diffX * movementScale;
257 if (newScale > 3.0f) newScale = 3.0f;
258 if (newScale < 0.5f) newScale = 0.5f;
259 if (newSpacing > 3.0f) newSpacing = 3.0f;
260 if (newSpacing < 0.5f) newSpacing = 0.5f;
261 pickedControl_->SetSpacing(newSpacing);
262 pickedControl_->SetScale(newScale);
265 if ((touch.flags & TOUCH_DOWN) && pickedControl_ == 0) {
266 pickedControl_ = getPickedControl(touch.x, touch.y);
267 if (pickedControl_) {
268 startX_ = touch.x;
269 startY_ = touch.y;
270 startSpacing_ = pickedControl_->GetSpacing();
271 startScale_ = pickedControl_->GetScale();
274 if ((touch.flags & TOUCH_UP) && pickedControl_ != 0) {
275 pickedControl_->SavePosition();
276 pickedControl_ = 0;
278 return true;
281 void TouchControlLayoutScreen::onFinish(DialogResult reason) {
282 g_Config.Save();
285 UI::EventReturn TouchControlLayoutScreen::OnVisibility(UI::EventParams &e) {
286 screenManager()->push(new TouchControlVisibilityScreen());
287 return UI::EVENT_DONE;
290 UI::EventReturn TouchControlLayoutScreen::OnReset(UI::EventParams &e) {
291 ILOG("Resetting touch control layout");
292 g_Config.ResetControlLayout();
293 const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
294 InitPadLayout(bounds.w, bounds.h);
295 RecreateViews();
296 return UI::EVENT_DONE;
299 void TouchControlLayoutScreen::dialogFinished(const Screen *dialog, DialogResult result) {
300 RecreateViews();
303 void TouchControlLayoutScreen::CreateViews() {
304 // setup g_Config for button layout
305 const Bounds &bounds = screenManager()->getUIContext()->GetBounds();
306 InitPadLayout(bounds.w, bounds.h);
308 local_dp_xres = bounds.w;
309 local_dp_yres = bounds.h;
311 using namespace UI;
313 I18NCategory *co = GetI18NCategory("Controls");
314 I18NCategory *di = GetI18NCategory("Dialog");
316 root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
318 Choice *reset = new Choice(di->T("Reset"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 84));
319 Choice *back = new Choice(di->T("Back"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 10));
320 Choice *visibility = new Choice(co->T("Visibility"), "", false, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 158));
321 // controlsSettings->Add(new PopupSliderChoiceFloat(&g_Config.fButtonScale, 0.80, 2.0, co->T("Button Scaling"), screenManager()))
322 // ->OnChange.Handle(this, &GameSettingsScreen::OnChangeControlScaling);
324 mode_ = new ChoiceStrip(ORIENT_VERTICAL, new AnchorLayoutParams(leftColumnWidth, WRAP_CONTENT, 10, NONE, NONE, 158 + 64 + 10));
325 mode_->AddChoice(di->T("Move"));
326 mode_->AddChoice(di->T("Resize"));
327 mode_->SetSelection(0);
329 reset->OnClick.Handle(this, &TouchControlLayoutScreen::OnReset);
330 back->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
331 visibility->OnClick.Handle(this, &TouchControlLayoutScreen::OnVisibility);
332 root_->Add(mode_);
333 root_->Add(visibility);
334 root_->Add(reset);
335 root_->Add(back);
337 TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, leftColumnWidth, new AnchorLayoutParams(10, 0, 10, 0, false));
338 root_->Add(tabHolder);
340 // this is more for show than anything else. It's used to provide a boundary
341 // so that buttons like back can be placed within the boundary.
342 // serves no other purpose.
343 AnchorLayout *controlsHolder = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
345 I18NCategory *ms = GetI18NCategory("MainSettings");
347 tabHolder->AddTab(ms->T("Controls"), controlsHolder);
349 if (!g_Config.bShowTouchControls) {
350 // Shouldn't even be able to get here as the way into this dialog should be closed.
351 return;
354 controls_.clear();
356 PSPActionButtons *actionButtons = new PSPActionButtons(g_Config.fActionButtonCenterX, g_Config.fActionButtonCenterY, g_Config.fActionButtonScale, g_Config.fActionButtonSpacing);
357 actionButtons->setCircleVisibility(g_Config.bShowTouchCircle);
358 actionButtons->setCrossVisibility(g_Config.bShowTouchCross);
359 actionButtons->setTriangleVisibility(g_Config.bShowTouchTriangle);
360 actionButtons->setSquareVisibility(g_Config.bShowTouchSquare);
362 controls_.push_back(actionButtons);
364 int rectImage = g_Config.iTouchButtonStyle ? I_RECT_LINE : I_RECT;
365 int shoulderImage = g_Config.iTouchButtonStyle ? I_SHOULDER_LINE : I_SHOULDER;
366 int dirImage = g_Config.iTouchButtonStyle ? I_DIR_LINE : I_DIR;
367 int stickImage = g_Config.iTouchButtonStyle ? I_STICK_LINE : I_STICK;
368 int stickBg = g_Config.iTouchButtonStyle ? I_STICK_BG_LINE : I_STICK_BG;
370 if (g_Config.bShowTouchDpad) {
371 controls_.push_back(new PSPDPadButtons(g_Config.fDpadX, g_Config.fDpadY, g_Config.fDpadScale, g_Config.fDpadSpacing));
374 if (g_Config.bShowTouchSelect) {
375 controls_.push_back(new DragDropButton(g_Config.fSelectKeyX, g_Config.fSelectKeyY, rectImage, I_SELECT, g_Config.fSelectKeyScale));
378 if (g_Config.bShowTouchStart) {
379 controls_.push_back(new DragDropButton(g_Config.fStartKeyX, g_Config.fStartKeyY, rectImage, I_START, g_Config.fStartKeyScale));
382 if (g_Config.bShowTouchUnthrottle) {
383 DragDropButton *unthrottle = new DragDropButton(g_Config.fUnthrottleKeyX, g_Config.fUnthrottleKeyY, rectImage, I_ARROW, g_Config.fUnthrottleKeyScale);
384 unthrottle->SetAngle(180.0f);
385 controls_.push_back(unthrottle);
388 if (g_Config.bShowTouchLTrigger) {
389 controls_.push_back(new DragDropButton(g_Config.fLKeyX, g_Config.fLKeyY, shoulderImage, I_L, g_Config.fLKeyScale));
392 if (g_Config.bShowTouchRTrigger) {
393 DragDropButton *rbutton = new DragDropButton(g_Config.fRKeyX, g_Config.fRKeyY, shoulderImage, I_R, g_Config.fRKeyScale);
394 rbutton->FlipImageH(true);
395 controls_.push_back(rbutton);
398 if (g_Config.bShowTouchAnalogStick) {
399 controls_.push_back(new DragDropButton(g_Config.fAnalogStickX, g_Config.fAnalogStickY, stickBg, stickImage, g_Config.fAnalogStickScale));
402 for (size_t i = 0; i < controls_.size(); i++) {
403 root_->Add(controls_[i]);
407 // return the control which was picked up by the touchEvent. If a control
408 // was already picked up, then it's being dragged around, so just return that instead
409 DragDropButton *TouchControlLayoutScreen::getPickedControl(const int x, const int y) {
410 if (pickedControl_ != 0) {
411 return pickedControl_;
414 for (size_t i = 0; i < controls_.size(); i++) {
415 DragDropButton *control = controls_[i];
416 const Bounds &bounds = control->GetBounds();
417 const float thresholdFactor = 1.5f;
419 Bounds tolerantBounds(bounds.x, bounds.y, bounds.w * thresholdFactor, bounds.h * thresholdFactor);
420 if (tolerantBounds.Contains(x, y)) {
421 return control;
425 return 0;