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-2019 The ManaPlus Developers
6  * Copyright (C) 2019-2021 Andrei Karas
7  *
8  * This file is part of The ManaPlus Client.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program. If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "gui/widgets/dropdown.h"
25 
26 #include "settings.h"
27 
28 #include "gui/gui.h"
29 #include "gui/skin.h"
30 
31 #include "gui/fonts/font.h"
32 
34 
36 #include "gui/widgets/popuplist.h"
37 
38 #include "render/graphics.h"
39 
40 #include "resources/imagerect.h"
41 
42 #include "debug.h"
43 
44 int DropDown::instances = 0;
47 float DropDown::mAlpha = 1.0;
48 Skin *DropDown::mSkin = nullptr;
49 
50 static std::string const dropdownFiles[2] =
51 {
52  "dropdown.xml",
53  "dropdown_pressed.xml"
54 };
55 
56 DropDown::DropDown(const Widget2 *const widget,
57  ListModel *const listModel,
58  const bool isExtended,
59  const Modal modal,
60  ActionListener *const listener,
61  const std::string &eventId) :
63  BasicContainer(widget),
64  KeyListener(),
65  MouseListener(),
66  FocusListener(),
68  mPopup(CREATEWIDGETR(PopupList, this, listModel, isExtended, modal)),
69  mShadowColor(getThemeColor(ThemeColorId::DROPDOWN_SHADOW, 255U)),
70  mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT, 255U)),
71  mPadding(1),
72  mImagePadding(2),
73  mSpacing(0),
74  mFoldedUpHeight(0),
75  mSelectionListeners(),
76  mExtended(isExtended),
77  mDroppedDown(false),
78  mPushed(false),
79  mIsDragged(false)
80 {
81  mAllowLogic = false;
82  mFrameSize = 2;
83  mForegroundColor2 = getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U);
84 
85  mPopup->setHeight(100);
86 
87  // Initialize graphics
88  if (instances == 0 && (theme != nullptr))
89  {
90  // Load the background skin
91  for (int i = 0; i < 2; i ++)
92  {
93  Skin *const skin = theme->load(dropdownFiles[i],
94  "dropdown.xml",
95  true,
97  if (skin != nullptr)
98  {
99  if (i == 0)
100  mSkin = skin;
101  const ImageRect &rect = skin->getBorder();
102  for (int f = 0; f < 2; f ++)
103  {
104  if (rect.grid[f] != nullptr)
105  {
106  rect.grid[f]->incRef();
107  buttons[f][i] = rect.grid[f];
108  buttons[f][i]->setAlpha(mAlpha);
109  }
110  else
111  {
112  buttons[f][i] = nullptr;
113  }
114  }
115  if (i != 0)
116  theme->unload(skin);
117  }
118  else
119  {
120  for (int f = 0; f < 2; f ++)
121  buttons[f][i] = nullptr;
122  }
123  }
124 
125  // get the border skin
126  theme->loadRect(skinRect, "dropdown_background.xml", "", 0, 8);
127  }
128 
129  instances++;
130 
131  setWidth(100);
132  setFocusable(true);
133  setListModel(listModel);
134 
135  if (mPopup->getSelected() < 0)
136  mPopup->setSelected(0);
137 
138  addMouseListener(this);
139  addKeyListener(this);
140  addFocusListener(this);
141 
142  adjustHeight();
143 // mPopup->setForegroundColorAll(getThemeColor(ThemeColorId::DROPDOWN,
144 // 255U),
145 // getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U));
146  mForegroundColor = getThemeColor(ThemeColorId::DROPDOWN, 255U);
147  mForegroundColor2 = getThemeColor(ThemeColorId::DROPDOWN_OUTLINE, 255U);
148 
149  if (!eventId.empty())
150  setActionEventId(eventId);
151 
152  if (listener != nullptr)
154 
155  mPopup->adjustSize();
156 
157  if (mSkin != nullptr)
158  {
159  mSpacing = mSkin->getOption("spacing");
160  mFrameSize = CAST_U32(mSkin->getOption("frameSize"));
162  mImagePadding = mSkin->getOption("imagePadding");
163  }
164  adjustHeight();
165 }
166 
168 {
169  if (gui != nullptr)
170  gui->removeDragged(this);
171 
172  instances--;
173  if (instances == 0)
174  {
175  for (int f = 0; f < 2; f ++)
176  {
177  for (int i = 0; i < 2; i ++)
178  {
179  if (buttons[f][i] != nullptr)
180  buttons[f][i]->decRef();
181  }
182  }
183  if (theme != nullptr)
184  {
185  theme->unload(mSkin);
187  }
188  }
189 }
190 
192 {
193  const float alpha = std::max(settings.guiAlpha,
195 
196  if (mAlpha != alpha)
197  {
198  mAlpha = alpha;
199 
200  if (buttons[0][0] != nullptr)
201  buttons[0][0]->setAlpha(mAlpha);
202  if (buttons[0][1] != nullptr)
203  buttons[0][1]->setAlpha(mAlpha);
204  if (buttons[1][0] != nullptr)
205  buttons[1][0]->setAlpha(mAlpha);
206  if (buttons[1][1] != nullptr)
207  buttons[1][1]->setAlpha(mAlpha);
208 
209  for (int a = 0; a < 9; a++)
210  {
211  if (skinRect.grid[a] != nullptr)
212  skinRect.grid[a]->setAlpha(mAlpha);
213  }
214  }
215 }
216 
217 void DropDown::draw(Graphics *const graphics)
218 {
219  BLOCK_START("DropDown::draw")
220  int h;
221 
222  if (mDroppedDown)
223  h = mFoldedUpHeight;
224  else
225  h = mDimension.height;
226 
227  updateAlpha();
228 
229  const unsigned int alpha = CAST_U32(mAlpha * 255.0F);
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  const int pad = 2 * mPadding;
276  graphics->setColor(mHighlightColor);
277  graphics->drawRectangle(Rect(mPadding, mPadding,
278  mDimension.width - h - pad, h - pad));
279  }
280 
281  drawButton(graphics);
282 
283  if (mDroppedDown)
284  {
285  // Draw two lines separating the ListBox with selected
286  // element view.
287  const int w = mDimension.width;
288  graphics->setColor(mHighlightColor);
289  graphics->drawLine(0, h, w, h);
290  graphics->setColor(mShadowColor);
291  graphics->drawLine(0, h + 1, w, h + 1);
292  }
293  BLOCK_END("DropDown::draw")
294 }
295 
296 void DropDown::safeDraw(Graphics *const graphics)
297 {
298  DropDown::draw(graphics);
299 }
300 
301 void DropDown::drawFrame(Graphics *const graphics)
302 {
303  BLOCK_START("DropDown::drawFrame")
304  const int bs2 = CAST_S32(getFrameSize());
305  const Rect &rect = mDimension;
306  graphics->drawImageRect(0, 0,
307  rect.width + bs2, rect.height + bs2,
308  skinRect);
309  BLOCK_END("DropDown::drawFrame")
310 }
311 
312 void DropDown::safeDrawFrame(Graphics *const graphics)
313 {
314  BLOCK_START("DropDown::drawFrame")
315  const int bs2 = CAST_S32(getFrameSize());
316  const Rect &rect = mDimension;
317  graphics->drawImageRect(0, 0,
318  rect.width + bs2, rect.height + bs2,
319  skinRect);
320  BLOCK_END("DropDown::drawFrame")
321 }
322 
323 void DropDown::drawButton(Graphics *const graphics)
324 {
325  Image *const image = buttons[mDroppedDown][mPushed];
326  if (image != nullptr)
327  {
328  const int height = mDroppedDown ? mFoldedUpHeight : mDimension.height;
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;
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 {
573 }
574 
576 {
578  iter != mSelectionListeners.end();
579  ++iter)
580  {
581  SelectionEvent event(this);
582  (*iter)->valueChanged(event);
583  }
584 }
#define CAST_S32
Definition: cast.h:30
#define CAST_U32
Definition: cast.h:31
unsigned int a
Definition: color.h:251
static Skin * mSkin
Definition: dropdown.h:164
bool mExtended
Definition: dropdown.h:154
std::string getSelectedString() const
Definition: dropdown.cpp:461
static int instances
Definition: dropdown.h:160
bool mDroppedDown
Definition: dropdown.h:155
int getSelected() const
Definition: dropdown.cpp:509
void drawFrame(Graphics *const graphics)
Definition: dropdown.cpp:301
bool mPushed
Definition: dropdown.h:156
void adjustHeight()
Definition: dropdown.cpp:470
int mSpacing
Definition: dropdown.h:147
void hideDrop(bool event)
Definition: dropdown.cpp:378
void action(const ActionEvent &actionEvent)
Definition: dropdown.cpp:535
void setSelected(int selected)
Definition: dropdown.cpp:514
void removeSelectionListener(SelectionListener *selectionListener)
Definition: dropdown.cpp:570
void valueChanged(const SelectionEvent &event)
Definition: dropdown.cpp:553
Color mShadowColor
Definition: dropdown.h:143
void keyPressed(KeyEvent &event)
Definition: dropdown.cpp:335
SelectionListenerList::iterator SelectionListenerIterator
Definition: dropdown.h:152
PopupList * mPopup
Definition: dropdown.h:142
Rect getChildrenArea()
Definition: dropdown.cpp:541
int mImagePadding
Definition: dropdown.h:146
void updateAlpha()
Definition: dropdown.cpp:191
void draw(Graphics *const graphics)
Definition: dropdown.cpp:217
void mousePressed(MouseEvent &event)
Definition: dropdown.cpp:385
static ImageRect skinRect
Definition: dropdown.h:162
void safeDraw(Graphics *const graphics)
Definition: dropdown.cpp:296
static float mAlpha
Definition: dropdown.h:163
Color mHighlightColor
Definition: dropdown.h:144
ListModel * getListModel()
Definition: dropdown.cpp:530
void mouseWheelMovedUp(MouseEvent &event)
Definition: dropdown.cpp:433
bool mIsDragged
Definition: dropdown.h:157
static Image * buttons[2][2]
Definition: dropdown.h:161
void dropDown()
Definition: dropdown.cpp:475
SelectionListenerList mSelectionListeners
Definition: dropdown.h:151
void mouseDragged(MouseEvent &event)
Definition: dropdown.cpp:427
void updateSelection()
Definition: dropdown.cpp:557
int mPadding
Definition: dropdown.h:145
void safeDrawFrame(Graphics *const graphics)
Definition: dropdown.cpp:312
int mFoldedUpHeight
Definition: dropdown.h:148
void setListModel(ListModel *const listModel)
Definition: dropdown.cpp:520
void foldUp()
Definition: dropdown.cpp:500
void drawButton(Graphics *const graphics)
Definition: dropdown.cpp:323
void mouseWheelMovedDown(MouseEvent &event)
Definition: dropdown.cpp:439
DropDown(const Widget2 *const widget, ListModel *const listModel, const bool extended, const Modal modal, ActionListener *const listener, const std::string &eventId)
Definition: dropdown.cpp:56
void distributeValueChangedEvent()
Definition: dropdown.cpp:575
void mouseReleased(MouseEvent &event)
Definition: dropdown.cpp:403
void setSelectedString(const std::string &str)
Definition: dropdown.cpp:445
void addSelectionListener(SelectionListener *listener)
Definition: dropdown.cpp:565
Widget * getSource() const
Definition: event.h:104
virtual const Image * getImageAt(int i)=0
Definition: font.h:90
void drawString(Graphics *const graphics, Color col, const Color &col2, const std::string &text, const int x, const int y)
Definition: font.cpp:254
virtual void drawImage(const Image *const image, int dstX, int dstY)=0
virtual void setColor(const Color &color)
Definition: graphics.h:320
virtual void drawRectangle(const Rect &rectangle)=0
virtual void drawLine(int x1, int y1, int x2, int y2)=0
virtual void drawImageRect(const int x, const int y, const int w, const int h, const ImageRect &imgRect)=0
void removeDragged(const Widget *const widget)
Definition: gui.cpp:1162
Image * grid[9]
Definition: imagerect.h:42
bool isConsumed() const
virtual std::string getElementAt(int i)=0
virtual int getNumberOfElements()=0
MouseButtonT getButton() const
Definition: mouseevent.h:116
void setSelected(const int selected)
Definition: popuplist.cpp:107
int getSelected() const
Definition: popuplist.cpp:115
void setListModel(ListModel *const model)
Definition: popuplist.cpp:123
void adjustSize()
Definition: popuplist.cpp:130
void show(int x, int y)
Definition: popuplist.cpp:82
ListModel * getListModel() const
Definition: popuplist.h:63
int getPadding() const
Definition: popup.h:135
Definition: rect.h:74
int width
Definition: rect.h:219
int height
Definition: rect.h:224
float guiAlpha
Definition: settings.h:131
Definition: skin.h:37
int getOption(const std::string &name) const
Definition: skin.h:106
const ImageRect & getBorder() const
Definition: skin.h:68
int getPadding() const
Definition: skin.h:100
void unload(Skin *const skin)
Definition: theme.cpp:250
static std::string getThemePath()
Definition: theme.h:67
static void unloadRect(const ImageRect &rect, const int start, const int end)
Definition: theme.cpp:915
Skin * load(const std::string &filename, const std::string &filename2, const bool full, const std::string &defaultPath)
Definition: theme.cpp:179
void loadRect(ImageRect &image, const std::string &name, const std::string &name2, const int start, const int end)
Definition: theme.cpp:883
float getMinimumOpacity() const
Definition: theme.h:124
Color mForegroundColor2
Definition: widget2.h:113
const Color & getThemeColor(const ThemeColorIdT type, const unsigned int alpha) const A_INLINE
Definition: widget2.h:45
void setVisible(Visible visible)
Definition: widget.cpp:225
unsigned int getFrameSize() const
Definition: widget.h:184
Color mForegroundColor
Definition: widget.h:1086
void setFocusable(const bool focusable)
Definition: widget.cpp:192
void setWidth(const int width)
Definition: widget.cpp:133
void distributeActionEvent()
Definition: widget.cpp:493
virtual void getAbsolutePosition(int &x, int &y) const
Definition: widget.cpp:312
virtual void requestMoveToTop()
Definition: widget.cpp:213
Widget * mParent
Definition: widget.h:1128
Rect mDimension
Definition: widget.h:1101
unsigned int mFrameSize
Definition: widget.h:1138
bool mAllowLogic
Definition: widget.h:1160
void addMouseListener(MouseListener *const mouseListener)
Definition: widget.cpp:292
void setHeight(const int height)
Definition: widget.cpp:140
void setActionEventId(const std::string &actionEventId)
Definition: widget.h:596
void addKeyListener(KeyListener *const keyListener)
Definition: widget.cpp:272
virtual void requestFocus()
Definition: widget.cpp:204
Font * getFont() const
Definition: widget.cpp:331
void addActionListener(ActionListener *const actionListener)
Definition: widget.cpp:252
void addFocusListener(FocusListener *const focusListener)
Definition: widget.cpp:282
int getHeight() const
Definition: widget.h:240
virtual bool isFocused() const
Definition: widget.cpp:184
#define CREATEWIDGETR(type,...)
Definition: createwidget.h:36
static std::string const dropdownFiles[2]
Definition: dropdown.cpp:50
PRAGMA45(GCC diagnostic push) PRAGMA45(GCC diagnostic ignored "-Wredundant-decls") PRAGMA45(GCC diagnostic pop) class TestMain
Gui * gui
Definition: gui.cpp:111
InputAction ::T InputActionT
Definition: inputaction.h:717
#define A_UNUSED
Definition: localconsts.h:160
bool Modal
Definition: modal.h:30
MouseButton ::T MouseButtonT
Definition: mousebutton.h:78
#define BLOCK_END(name)
Definition: perfomance.h:80
#define BLOCK_START(name)
Definition: perfomance.h:79
Settings settings
Definition: settings.cpp:32
Theme * theme
Definition: theme.cpp:62
const bool Visible_false
Definition: visible.h:30