ManaPlus
dropdown.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2006-2009 The Mana World Development Team
4  * Copyright (C) 2009-2010 The Mana Developers
5  * Copyright (C) 2011-2018 The ManaPlus Developers
6  *
7  * This file is part of The ManaPlus Client.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program. If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 #include "gui/widgets/dropdown.h"
24 
25 #include "settings.h"
26 
27 #include "gui/gui.h"
28 #include "gui/skin.h"
29 
30 #include "gui/fonts/font.h"
31 
33 
35 #include "gui/widgets/popuplist.h"
36 
37 #include "render/graphics.h"
38 
39 #include "resources/imagerect.h"
40 
41 #include "debug.h"
42 
43 int DropDown::instances = 0;
46 float DropDown::mAlpha = 1.0;
47 Skin *DropDown::mSkin = nullptr;
48 
49 static std::string const dropdownFiles[2] =
50 {
51  "dropdown.xml",
52  "dropdown_pressed.xml"
53 };
54 
55 DropDown::DropDown(const Widget2 *const widget,
56  ListModel *const listModel,
57  const bool isExtended,
58  const Modal modal,
59  ActionListener *const listener,
60  const std::string &eventId) :
62  BasicContainer(widget),
63  KeyListener(),
64  MouseListener(),
65  FocusListener(),
67  mPopup(CREATEWIDGETR(PopupList, this, listModel, isExtended, modal)),
68  mShadowColor(getThemeColor(ThemeColorId::DROPDOWN_SHADOW, 255U)),
69  mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT, 255U)),
70  mPadding(1),
71  mImagePadding(2),
72  mSpacing(0),
73  mFoldedUpHeight(0),
74  mSelectionListeners(),
75  mExtended(isExtended),
76  mDroppedDown(false),
77  mPushed(false),
78  mIsDragged(false)
79 {
80  mAllowLogic = false;
81  mFrameSize = 2;
83 
84  mPopup->setHeight(100);
85 
86  // Initialize graphics
87  if (instances == 0 && (theme != nullptr))
88  {
89  // Load the background skin
90  for (int i = 0; i < 2; i ++)
91  {
92  Skin *const skin = theme->load(dropdownFiles[i],
93  "dropdown.xml",
94  true,
96  if (skin != nullptr)
97  {
98  if (i == 0)
99  mSkin = skin;
100  const ImageRect &rect = skin->getBorder();
101  for (int f = 0; f < 2; f ++)
102  {
103  if (rect.grid[f] != nullptr)
104  {
105  rect.grid[f]->incRef();
106  buttons[f][i] = rect.grid[f];
107  buttons[f][i]->setAlpha(mAlpha);
108  }
109  else
110  {
111  buttons[f][i] = nullptr;
112  }
113  }
114  if (i != 0)
115  theme->unload(skin);
116  }
117  else
118  {
119  for (int f = 0; f < 2; f ++)
120  buttons[f][i] = nullptr;
121  }
122  }
123 
124  // get the border skin
125  theme->loadRect(skinRect, "dropdown_background.xml", "", 0, 8);
126  }
127 
128  instances++;
129 
130  setWidth(100);
131  setFocusable(true);
132  setListModel(listModel);
133 
134  if (mPopup->getSelected() < 0)
135  mPopup->setSelected(0);
136 
137  addMouseListener(this);
138  addKeyListener(this);
139  addFocusListener(this);
140 
141  adjustHeight();
142 // mPopup->setForegroundColorAll(getThemeColor(ThemeColorId::DROPDOWN,
143 // 255U),
144 // getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U));
147 
148  if (!eventId.empty())
149  setActionEventId(eventId);
150 
151  if (listener != nullptr)
152  addActionListener(listener);
153 
154  mPopup->adjustSize();
155 
156  if (mSkin != nullptr)
157  {
158  mSpacing = mSkin->getOption("spacing");
159  mFrameSize = CAST_U32(mSkin->getOption("frameSize"));
161  mImagePadding = mSkin->getOption("imagePadding");
162  }
163  adjustHeight();
164 }
165 
167 {
168  if (gui != nullptr)
169  gui->removeDragged(this);
170 
171  instances--;
172  if (instances == 0)
173  {
174  for (int f = 0; f < 2; f ++)
175  {
176  for (int i = 0; i < 2; i ++)
177  {
178  if (buttons[f][i] != nullptr)
179  buttons[f][i]->decRef();
180  }
181  }
182  if (theme != nullptr)
183  {
184  theme->unload(mSkin);
186  }
187  }
188 }
189 
191 {
192  const float alpha = std::max(settings.guiAlpha,
194 
195  if (mAlpha != alpha)
196  {
197  mAlpha = alpha;
198 
199  if (buttons[0][0] != nullptr)
200  buttons[0][0]->setAlpha(mAlpha);
201  if (buttons[0][1] != nullptr)
202  buttons[0][1]->setAlpha(mAlpha);
203  if (buttons[1][0] != nullptr)
204  buttons[1][0]->setAlpha(mAlpha);
205  if (buttons[1][1] != nullptr)
206  buttons[1][1]->setAlpha(mAlpha);
207 
208  for (int a = 0; a < 9; a++)
209  {
210  if (skinRect.grid[a] != nullptr)
212  }
213  }
214 }
215 
216 void DropDown::draw(Graphics *const graphics)
217 {
218  BLOCK_START("DropDown::draw")
219  int h;
220 
221  if (mDroppedDown)
222  h = mFoldedUpHeight;
223  else
224  h = mDimension.height;
225 
226  updateAlpha();
227 
228  const unsigned int alpha = CAST_U32(mAlpha * 255.0F);
229  const int pad = 2 * mPadding;
230  mHighlightColor.a = alpha;
231  mShadowColor.a = alpha;
232 
233  ListModel *const model = mPopup->getListModel();
234  if ((model != nullptr) && mPopup->getSelected() >= 0)
235  {
236  Font *const font = getFont();
237  if (mExtended)
238  {
239  const int sel = mPopup->getSelected();
240  ExtendedListModel *const model2
241  = static_cast<ExtendedListModel *>(model);
242  const Image *const image = model2->getImageAt(sel);
243  if (image == nullptr)
244  {
245  font->drawString(graphics,
248  model->getElementAt(sel),
249  mPadding, mPadding);
250  }
251  else
252  {
253  graphics->drawImage(image,
255  (mDimension.height - image->getHeight()) / 2 + mPadding);
256  font->drawString(graphics,
259  model->getElementAt(sel),
260  image->getWidth() + mImagePadding + mSpacing, mPadding);
261  }
262  }
263  else
264  {
265  font->drawString(graphics,
268  model->getElementAt(mPopup->getSelected()),
269  mPadding, mPadding);
270  }
271  }
272 
273  if (isFocused())
274  {
275  graphics->setColor(mHighlightColor);
276  graphics->drawRectangle(Rect(mPadding, mPadding,
277  mDimension.width - h - pad, h - pad));
278  }
279 
280  drawButton(graphics);
281 
282  if (mDroppedDown)
283  {
284  // Draw two lines separating the ListBox with selected
285  // element view.
286  const int w = mDimension.width;
287  graphics->setColor(mHighlightColor);
288  graphics->drawLine(0, h, w, h);
289  graphics->setColor(mShadowColor);
290  graphics->drawLine(0, h + 1, w, h + 1);
291  }
292  BLOCK_END("DropDown::draw")
293 }
294 
295 void DropDown::safeDraw(Graphics *const graphics)
296 {
297  DropDown::draw(graphics);
298 }
299 
300 void DropDown::drawFrame(Graphics *const graphics)
301 {
302  BLOCK_START("DropDown::drawFrame")
303  const int bs2 = CAST_S32(getFrameSize());
304  const Rect &rect = mDimension;
305  graphics->drawImageRect(0, 0,
306  rect.width + bs2, rect.height + bs2,
307  skinRect);
308  BLOCK_END("DropDown::drawFrame")
309 }
310 
311 void DropDown::safeDrawFrame(Graphics *const graphics)
312 {
313  BLOCK_START("DropDown::drawFrame")
314  const int bs2 = CAST_S32(getFrameSize());
315  const Rect &rect = mDimension;
316  graphics->drawImageRect(0, 0,
317  rect.width + bs2, rect.height + bs2,
318  skinRect);
319  BLOCK_END("DropDown::drawFrame")
320 }
321 
322 void DropDown::drawButton(Graphics *const graphics)
323 {
324  const int height = mDroppedDown ? mFoldedUpHeight : mDimension.height;
325 
326  Image *image = buttons[mDroppedDown][mPushed];
327  if (image != nullptr)
328  {
329  graphics->drawImage(image,
330  mDimension.width - image->getWidth() - mImagePadding,
331  (height - image->getHeight()) / 2);
332  }
333 }
334 
336 {
337  if (event.isConsumed())
338  return;
339 
340  const InputActionT actionId = event.getActionId();
341  PRAGMA45(GCC diagnostic push)
342  PRAGMA45(GCC diagnostic ignored "-Wswitch-enum")
343  switch (actionId)
344  {
347  dropDown();
348  break;
349 
350  case InputAction::GUI_UP:
351  setSelected(getSelected() - 1);
352  break;
353 
355  setSelected(getSelected() + 1);
356  break;
357 
359  setSelected(0);
360  break;
361 
363  if (mPopup->getListModel() != nullptr)
364  {
366  getNumberOfElements() - 1);
367  }
368  break;
369 
370  default:
371  return;
372  }
373  PRAGMA45(GCC diagnostic pop)
374 
375  event.consume();
376 }
377 
378 void DropDown::hideDrop(bool event)
379 {
380  if (event)
383 }
384 
386 {
387  event.consume();
388  // If we have a mouse press on the widget.
389  if (event.getButton() == MouseButton::LEFT
390  && !mDroppedDown && event.getSource() == this)
391  {
392  mPushed = true;
393  dropDown();
394  }
395  else
396  {
397  mPushed = false;
398  foldUp();
399  hideDrop(true);
400  }
401 }
402 
404 {
405  if (mIsDragged)
406  mPushed = false;
407 
408  const MouseButtonT button = event.getButton();
409  const int x = event.getX();
410  const int y = event.getY();
411  // Released outside of widget. Can happen when we have modal
412  // input focus.
413  if ((0 > y || y >= mDimension.height || x < 0 || x >= mDimension.width)
414  && button == MouseButton::LEFT)
415  {
416  if (mIsDragged)
417  foldUp();
418  }
419  else if (button == MouseButton::LEFT)
420  {
421  mPushed = false;
422  }
423 
424  mIsDragged = false;
425 }
426 
428 {
429  mIsDragged = true;
430  event.consume();
431 }
432 
434 {
435  setSelected(getSelected() - 1);
436  event.consume();
437 }
438 
440 {
441  setSelected(getSelected() + 1);
442  event.consume();
443 }
444 
445 void DropDown::setSelectedString(const std::string &str)
446 {
447  ListModel *const listModel = mPopup->getListModel();
448  if (listModel == nullptr)
449  return;
450 
451  for (int f = 0; f < listModel->getNumberOfElements(); f ++)
452  {
453  if (listModel->getElementAt(f) == str)
454  {
455  setSelected(f);
456  break;
457  }
458  }
459 }
460 
461 std::string DropDown::getSelectedString() const
462 {
463  ListModel *const listModel = mPopup->getListModel();
464  if (listModel == nullptr)
465  return "";
466 
467  return listModel->getElementAt(getSelected());
468 }
469 
471 {
472  setHeight(getFont()->getHeight() + 2 * mPadding);
473 }
474 
476 {
477  if (!mDroppedDown)
478  {
479  if (mParent == nullptr)
480  return;
481  mDroppedDown = true;
483  adjustHeight();
484 
485  int x = 0;
486  int y = 0;
487  getAbsolutePosition(x, y);
488  const int frame = CAST_S32(mParent->getFrameSize());
489  const int pad = mPopup->getPadding();
490  const int pad2 = pad * 2;
491 
492  // here width should be adjusted on some other parameters
493  mPopup->setWidth(mDimension.width - pad2 + 8);
494  mPopup->show(x - mPadding - frame - 1, y + mDimension.height);
496  mPopup->requestFocus();
497  }
498 }
499 
501 {
502  if (mDroppedDown)
503  {
504  mDroppedDown = false;
505  adjustHeight();
506  }
507 }
508 
510 {
511  return mPopup->getSelected();
512 }
513 
514 void DropDown::setSelected(int selected)
515 {
516  if (selected >= 0)
517  mPopup->setSelected(selected);
518 }
519 
520 void DropDown::setListModel(ListModel *const listModel)
521 {
522  mPopup->setListModel(listModel);
523 
524  if (mPopup->getSelected() < 0)
525  mPopup->setSelected(0);
526 
527  adjustHeight();
528 }
529 
531 {
532  return mPopup->getListModel();
533 }
534 
535 void DropDown::action(const ActionEvent &actionEvent A_UNUSED)
536 {
537  foldUp();
539 }
540 
542 {
543  if (mDroppedDown)
544  {
545  // Calculate the children area (with the one pixel border in mind)
546  return Rect(1, mFoldedUpHeight + 1,
548  }
549 
550  return Rect();
551 }
552 
554 {
555 }
556 
558 {
559  mDroppedDown = false;
560  mPushed = false;
563 }
564 
566 {
567  mSelectionListeners.push_back(selectionListener);
568 }
569 
571 {
572  mSelectionListeners.remove(listener);
573 }
574 
576 {
578  iter != mSelectionListeners.end();
579  ++iter)
580  {
581  SelectionEvent event(this);
582  (*iter)->valueChanged(event);
583  }
584 }
#define CAST_U32
Definition: cast.h:30
void removeSelectionListener(SelectionListener *selectionListener)
Definition: dropdown.cpp:570
int getPadding() const
Definition: popup.h:134
Font * getFont() const
Definition: widget.cpp:330
void setSelected(const int selected)
Definition: popuplist.cpp:106
int mFoldedUpHeight
Definition: dropdown.h:147
void loadRect(ImageRect &image, const std::string &name, const std::string &name2, const int start, const int end)
Definition: theme.cpp:882
int width
Definition: rect.h:218
void safeDrawFrame(Graphics *const graphics)
Definition: dropdown.cpp:311
int getSelected() const
Definition: popuplist.cpp:114
Definition: skin.h:35
void setWidth(const int width)
Definition: widget.cpp:132
void unload(Skin *const skin)
Definition: theme.cpp:249
Gui * gui
Definition: gui.cpp:110
Definition: font.h:88
Color mHighlightColor
Definition: dropdown.h:143
unsigned int a
Definition: color.h:250
PRAGMA45(GCC diagnostic push) PRAGMA45(GCC diagnostic ignored "-Wunused-result") int TestLauncher
void valueChanged(const SelectionEvent &event)
Definition: dropdown.cpp:553
Color mShadowColor
Definition: dropdown.h:142
virtual void drawImageRect(const int x, const int y, const int w, const int h, const ImageRect &imgRect)=0
void drawButton(Graphics *const graphics)
Definition: dropdown.cpp:322
void hideDrop(bool event)
Definition: dropdown.cpp:378
void action(const ActionEvent &actionEvent)
Definition: dropdown.cpp:535
void addActionListener(ActionListener *const actionListener)
Definition: widget.cpp:251
void setVisible(Visible visible)
Definition: widget.cpp:224
void show(int x, int y)
Definition: popuplist.cpp:81
MouseButtonT getButton() const
Definition: mouseevent.h:115
float getMinimumOpacity() const
Definition: theme.h:123
Definition: rect.h:72
virtual void requestMoveToTop()
Definition: widget.cpp:212
SelectionListenerList mSelectionListeners
Definition: dropdown.h:150
#define BLOCK_START(name)
Definition: perfomance.h:78
void addFocusListener(FocusListener *const focusListener)
Definition: widget.cpp:281
void mousePressed(MouseEvent &event)
Definition: dropdown.cpp:385
static std::string getThemePath()
Definition: theme.h:66
ListModel * getListModel()
Definition: dropdown.cpp:530
SelectionListenerList::iterator SelectionListenerIterator
Definition: dropdown.h:151
void updateAlpha()
Definition: dropdown.cpp:190
Skin * load(const std::string &filename, const std::string &filename2, const bool full, const std::string &defaultPath)
Definition: theme.cpp:178
#define BLOCK_END(name)
Definition: perfomance.h:79
virtual int getNumberOfElements()=0
void setActionEventId(const std::string &actionEventId)
Definition: widget.h:595
void mouseWheelMovedDown(MouseEvent &event)
Definition: dropdown.cpp:439
void setListModel(ListModel *const model)
Definition: popuplist.cpp:122
Color mForegroundColor
Definition: widget.h:1085
virtual void drawRectangle(const Rect &rectangle)=0
void keyPressed(KeyEvent &event)
Definition: dropdown.cpp:335
int getOption(const std::string &name) const
Definition: skin.h:105
virtual bool isFocused() const
Definition: widget.cpp:183
static int instances
Definition: dropdown.h:159
unsigned int getFrameSize() const
Definition: widget.h:183
void draw(Graphics *const graphics)
Definition: dropdown.cpp:216
void distributeActionEvent()
Definition: widget.cpp:492
static Skin * mSkin
Definition: dropdown.h:163
PopupList * mPopup
Definition: dropdown.h:141
void setSelectedString(const std::string &str)
Definition: dropdown.cpp:445
Settings settings
Definition: settings.cpp:31
virtual void setAlpha(const float alpha)
Definition: image.cpp:286
int getPadding() const
Definition: skin.h:99
bool mDroppedDown
Definition: dropdown.h:154
void setListModel(ListModel *const listModel)
Definition: dropdown.cpp:520
virtual void setColor(const Color &color)
Definition: graphics.h:319
#define CAST_S32
Definition: cast.h:29
virtual void requestFocus()
Definition: widget.cpp:203
int mPadding
Definition: dropdown.h:144
const ImageRect & getBorder() const
Definition: skin.h:67
void updateSelection()
Definition: dropdown.cpp:557
Image * grid[9]
Definition: imagerect.h:41
void adjustHeight()
Definition: dropdown.cpp:470
const bool Visible_false
Definition: visible.h:29
Widget * mParent
Definition: widget.h:1127
virtual const Image * getImageAt(int i)=0
unsigned int mFrameSize
Definition: widget.h:1137
void drawString(Graphics *const graphics, Color col, const Color &col2, const std::string &text, const int x, const int y)
Definition: font.cpp:253
void drawFrame(Graphics *const graphics)
Definition: dropdown.cpp:300
InputAction ::T InputActionT
Definition: inputaction.h:715
bool mAllowLogic
Definition: widget.h:1159
void addMouseListener(MouseListener *const mouseListener)
Definition: widget.cpp:291
void dropDown()
Definition: dropdown.cpp:475
int height
Definition: rect.h:223
void addKeyListener(KeyListener *const keyListener)
Definition: widget.cpp:271
void mouseDragged(MouseEvent &event)
Definition: dropdown.cpp:427
virtual void getAbsolutePosition(int &x, int &y) const
Definition: widget.cpp:311
bool isConsumed() const
virtual std::string getElementAt(int i)=0
bool mIsDragged
Definition: dropdown.h:156
std::string getSelectedString() const
Definition: dropdown.cpp:461
void foldUp()
Definition: dropdown.cpp:500
bool mPushed
Definition: dropdown.h:155
void setHeight(const int height)
Definition: widget.cpp:139
virtual void incRef()
Definition: resource.cpp:37
void mouseWheelMovedUp(MouseEvent &event)
Definition: dropdown.cpp:433
Theme * theme
Definition: theme.cpp:61
#define A_UNUSED
Definition: localconsts.h:151
void setFocusable(const bool focusable)
Definition: widget.cpp:191
void addSelectionListener(SelectionListener *listener)
Definition: dropdown.cpp:565
Rect getChildrenArea()
Definition: dropdown.cpp:541
Color mForegroundColor2
Definition: widget2.h:112
virtual void drawImage(const Image *const image, int dstX, int dstY)=0
void removeDragged(const Widget *const widget)
Definition: gui.cpp:1160
virtual void drawLine(int x1, int y1, int x2, int y2)=0
static ImageRect skinRect
Definition: dropdown.h:161
void mouseReleased(MouseEvent &event)
Definition: dropdown.cpp:403
Definition: image.h:61
int getHeight() const
Definition: widget.h:239
int getWidth() const A_INLINE
Definition: image.h:116
const Color & getThemeColor(const ThemeColorIdT type, const unsigned int alpha) const A_INLINE
Definition: widget2.h:44
void distributeValueChangedEvent()
Definition: dropdown.cpp:575
static Image * buttons[2][2]
Definition: dropdown.h:160
ListModel * getListModel() const
Definition: popuplist.h:62
#define CREATEWIDGETR(type,...)
Definition: createwidget.h:35
void adjustSize()
Definition: popuplist.cpp:129
static void unloadRect(const ImageRect &rect, const int start, const int end)
Definition: theme.cpp:914
float guiAlpha
Definition: settings.h:129
MouseButton ::T MouseButtonT
Definition: mousebutton.h:77
DropDown(const Widget2 *const widget, ListModel *const listModel, const bool extended, const Modal modal, ActionListener *const listener, const std::string &eventId)
Definition: dropdown.cpp:55
Rect mDimension
Definition: widget.h:1100
void setSelected(int selected)
Definition: dropdown.cpp:514
int mSpacing
Definition: dropdown.h:146
void decRef()
Definition: image.cpp:522
bool mExtended
Definition: dropdown.h:153
int getSelected() const
Definition: dropdown.cpp:509
static float mAlpha
Definition: dropdown.h:162
void safeDraw(Graphics *const graphics)
Definition: dropdown.cpp:295
int mImagePadding
Definition: dropdown.h:145
int getHeight() const A_INLINE
Definition: image.h:122