ManaPlus
listbox.cpp
Go to the documentation of this file.
1 /*
2  * The ManaPlus Client
3  * Copyright (C) 2004-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 /* _______ __ __ __ ______ __ __ _______ __ __
25  * / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___ /\ / |\/ /\
26  * / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
27  * / / /__ / / // / // / // / / / ___ / // ___ / // /| ' / /
28  * / /_// /\ / /_// / // / // /_/_ / / // / // /\_/ / // / | / /
29  * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
30  * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
31  *
32  * Copyright (c) 2004 - 2008 Olof Naessén and Per Larsson
33  *
34  *
35  * Per Larsson a.k.a finalman
36  * Olof Naessén a.k.a jansem/yakslem
37  *
38  * Visit: http://guichan.sourceforge.net
39  *
40  * License: (BSD)
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  * notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  * notice, this list of conditions and the following disclaimer in
48  * the documentation and/or other materials provided with the
49  * distribution.
50  * 3. Neither the name of Guichan nor the names of its contributors may
51  * be used to endorse or promote products derived from this software
52  * without specific prior written permission.
53  *
54  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
60  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
61  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
62  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
63  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
64  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65  */
66 
67 #include "gui/widgets/listbox.h"
68 
69 #include "settings.h"
70 
71 #include "gui/focushandler.h"
72 #include "gui/gui.h"
73 #include "gui/skin.h"
74 
75 #include "gui/fonts/font.h"
76 
77 #include "gui/models/listmodel.h"
78 
80 
81 #include "utils/foreach.h"
82 
83 #include "render/graphics.h"
84 
85 #include "debug.h"
86 
87 float ListBox::mAlpha = 1.0;
88 
89 ListBox::ListBox(const Widget2 *const widget,
90  ListModel *const listModel,
91  const std::string &skin) :
92  Widget(widget),
93  MouseListener(),
94  KeyListener(),
95  mSelected(-1),
96  mListModel(listModel),
97  mWrappingEnabled(false),
98  mSelectionListeners(),
99  mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT, 255U)),
100  mForegroundSelectedColor(getThemeColor(ThemeColorId::LISTBOX_SELECTED,
101  255U)),
102  mForegroundSelectedColor2(getThemeColor(
103  ThemeColorId::LISTBOX_SELECTED_OUTLINE, 255U)),
104  mOldSelected(-1),
105  mPadding(0),
106  mPressedIndex(-2),
107  mRowHeight(0),
108  mItemPadding(1),
109  mSkin(nullptr),
110  mDistributeMousePressed(true),
111  mCenterText(false)
112 {
113  setWidth(100);
114  setFocusable(true);
115  addMouseListener(this);
116  addKeyListener(this);
117 
118  mForegroundColor = getThemeColor(ThemeColorId::LISTBOX, 255U);
119  mForegroundColor2 = getThemeColor(ThemeColorId::LISTBOX_OUTLINE, 255U);
120 
121  if (theme != nullptr)
122  {
123  mSkin = theme->load(skin,
124  "listbox.xml",
125  true,
127  }
128 
129  if (mSkin != nullptr)
130  {
132  mItemPadding = mSkin->getOption("itemPadding");
133  }
134 
135  const Font *const font = getFont();
137  font->getHeight() + 2 * mItemPadding);
138 }
139 
141 {
142  adjustSize();
143 }
144 
146 {
147  if (gui != nullptr)
148  gui->removeDragged(this);
149 
150  if (theme != nullptr)
151  theme->unload(mSkin);
152 }
153 
155 {
156  const float alpha = std::max(settings.guiAlpha,
158 
159  if (mAlpha != alpha)
160  mAlpha = alpha;
161 }
162 
163 void ListBox::draw(Graphics *const graphics)
164 {
165  if (mListModel == nullptr)
166  return;
167 
168  BLOCK_START("ListBox::draw")
169  updateAlpha();
170 
171  mHighlightColor.a = CAST_U32(mAlpha * 255.0F);
172  graphics->setColor(mHighlightColor);
173  Font *const font = getFont();
174  const int rowHeight = CAST_S32(getRowHeight());
175  const int width = mDimension.width;
176 
177  if (mCenterText)
178  {
179  // Draw filled rectangle around the selected list element
180  if (mSelected >= 0)
181  {
182  graphics->fillRectangle(Rect(mPadding,
183  rowHeight * mSelected + mPadding,
184  mDimension.width - 2 * mPadding, rowHeight));
185 
186  const std::string str = mListModel->getElementAt(mSelected);
187  font->drawString(graphics,
190  str,
191  (width - font->getWidth(str)) / 2,
192  mSelected * rowHeight + mPadding + mItemPadding);
193  }
194  // Draw the list elements
195  const int sz = mListModel->getNumberOfElements();
196  for (int i = 0, y = mPadding + mItemPadding;
197  i < sz; ++i, y += rowHeight)
198  {
199  if (i != mSelected)
200  {
201  const std::string str = mListModel->getElementAt(i);
202  font->drawString(graphics,
205  str,
206  (width - font->getWidth(str)) / 2, y);
207  }
208  }
209  }
210  else
211  {
212  // Draw filled rectangle around the selected list element
213  if (mSelected >= 0)
214  {
215  graphics->fillRectangle(Rect(mPadding,
216  rowHeight * mSelected + mPadding,
217  mDimension.width - 2 * mPadding, rowHeight));
218 
219  const std::string str = mListModel->getElementAt(mSelected);
220  font->drawString(graphics,
223  str,
224  mPadding,
225  mSelected * rowHeight + mPadding + mItemPadding);
226  }
227  // Draw the list elements
228  const int sz = mListModel->getNumberOfElements();
229  for (int i = 0, y = mPadding + mItemPadding; i < sz;
230  ++i, y += rowHeight)
231  {
232  if (i != mSelected)
233  {
234  const std::string str = mListModel->getElementAt(i);
235  font->drawString(graphics,
238  str,
239  mPadding, y);
240  }
241  }
242  }
243  BLOCK_END("ListBox::draw")
244 }
245 
247 {
248  const InputActionT action = event.getActionId();
249  if (action == InputAction::GUI_SELECT)
250  {
252  event.consume();
253  }
254  else if (action == InputAction::GUI_UP)
255  {
256  if (mSelected > 0)
257  {
258  setSelected(mSelected - 1);
259  }
260  else if (mSelected == 0 &&
262  getListModel() != nullptr)
263  {
264  setSelected(getListModel()->getNumberOfElements() - 1);
265  }
266  event.consume();
267  }
268  else if (action == InputAction::GUI_DOWN)
269  {
270  const int num = getListModel()->getNumberOfElements() - 1;
271  if (mSelected < num)
272  setSelected(mSelected + 1);
273  else if (mSelected == num && mWrappingEnabled)
274  setSelected(0);
275  event.consume();
276  }
277  else if (action == InputAction::GUI_HOME)
278  {
279  setSelected(0);
280  event.consume();
281  }
282  else if (action == InputAction::GUI_END && (getListModel() != nullptr))
283  {
284  setSelected(getListModel()->getNumberOfElements() - 1);
285  event.consume();
286  }
287 }
288 
289 void ListBox::safeDraw(Graphics *const graphics)
290 {
291  ListBox::draw(graphics);
292 }
293 
294 // Don't do anything on scrollwheel. ScrollArea will deal with that.
295 
297 {
298 }
299 
301 {
302 }
303 
305 {
307  if (mMouseConsume && mPressedIndex != -1)
308  event.consume();
309 }
310 
312 {
313  if (mPressedIndex != getSelectionByMouse(event.getY()))
314  return;
315 
317  {
318  mouseReleased1(event);
319  }
320  else
321  {
322  switch (event.getClickCount())
323  {
324  case 1:
325  mouseDragged(event);
327  break;
328  case 2:
329  if (gui != nullptr)
330  gui->resetClickCount();
331  if (mOldSelected == mSelected)
332  mouseReleased1(event);
333  else
334  mouseDragged(event);
336  break;
337  default:
338  mouseDragged(event);
340  break;
341  }
342  }
343  mPressedIndex = -2;
344 }
345 
347 {
348  if (event.getButton() == MouseButton::LEFT)
349  {
350  setSelected(std::max(0, getSelectionByMouse(event.getY())));
352  }
353 }
354 
356 {
357  if (event.getButton() != MouseButton::LEFT || getRowHeight() == 0)
358  return;
359 
360  // Make list selection update on drag, but guard against negative y
361  if (getRowHeight() != 0U)
362  setSelected(std::max(0, getSelectionByMouse(event.getY())));
363 }
364 
366 {
367  if (mFocusHandler == nullptr)
368  return;
369 
370  if (isFocusable())
372 }
373 
375 {
376  BLOCK_START("ListBox::adjustSize")
377  if (mListModel != nullptr)
378  {
381  }
382  BLOCK_END("ListBox::adjustSize")
383 }
384 
386 {
387  BLOCK_START("ListBox::logic")
388  adjustSize();
389  BLOCK_END("ListBox::logic")
390 }
391 
392 int ListBox::getSelectionByMouse(const int y) const
393 {
394  if (y < mPadding)
395  return -1;
396  return (y - mPadding) / CAST_S32(getRowHeight());
397 }
398 
399 void ListBox::setSelected(const int selected)
400 {
401  if (mListModel == nullptr)
402  {
403  mSelected = -1;
404  }
405  else
406  {
407  if (selected < 0)
408  mSelected = -1;
409  else if (selected >= mListModel->getNumberOfElements())
411  else
412  mSelected = selected;
413  }
414 
415  Rect scroll;
416 
417  if (mSelected < 0)
418  scroll.y = 0;
419  else
420  scroll.y = CAST_S32(getRowHeight()) * mSelected;
421 
422  scroll.height = CAST_S32(getRowHeight());
423  showPart(scroll);
424 
426 }
427 
428 void ListBox::setListModel(ListModel *const listModel)
429 {
430  mSelected = -1;
431  mListModel = listModel;
432  adjustSize();
433 }
434 
435 void ListBox::addSelectionListener(SelectionListener *const selectionListener)
436 {
437  mSelectionListeners.push_back(selectionListener);
438 }
439 
441  selectionListener)
442 {
443  mSelectionListeners.remove(selectionListener);
444 }
445 
447 {
449  {
450  SelectionEvent event(this);
451  (*iter)->valueChanged(event);
452  }
453 }
#define CAST_S32
Definition: cast.h:30
#define CAST_U32
Definition: cast.h:31
unsigned int a
Definition: color.h:251
void requestFocus(const Widget *const widget)
Definition: font.h:90
int getHeight() const
Definition: font.cpp:362
int getWidth(const std::string &text) const
Definition: font.cpp:334
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 fillRectangle(const Rect &rectangle)=0
virtual void setColor(const Color &color)
Definition: graphics.h:320
void resetClickCount()
Definition: gui.cpp:980
void removeDragged(const Widget *const widget)
Definition: gui.cpp:1162
int mSelected
Definition: listbox.h:256
void setListModel(ListModel *listModel)
Definition: listbox.cpp:428
ListBox(const Widget2 *const widget, ListModel *const listModel, const std::string &skin)
Definition: listbox.cpp:89
bool mWrappingEnabled
Definition: listbox.h:266
void logic()
Definition: listbox.cpp:385
bool mCenterText
Definition: listbox.h:294
SelectionListenerList mSelectionListeners
Definition: listbox.h:276
void mouseReleased1(const MouseEvent &event)
Definition: listbox.cpp:346
Color mHighlightColor
Definition: listbox.h:283
void mouseWheelMovedDown(MouseEvent &event)
Definition: listbox.cpp:300
void mouseDragged(MouseEvent &event)
Definition: listbox.cpp:355
void mouseWheelMovedUp(MouseEvent &event)
Definition: listbox.cpp:296
void distributeValueChangedEvent()
Definition: listbox.cpp:446
void updateAlpha()
Definition: listbox.cpp:154
void mousePressed(MouseEvent &event)
Definition: listbox.cpp:304
unsigned int mRowHeight
Definition: listbox.h:289
virtual unsigned int getRowHeight() const
Definition: listbox.h:156
int mPressedIndex
Definition: listbox.h:288
void draw(Graphics *const graphics)
Definition: listbox.cpp:163
~ListBox()
Definition: listbox.cpp:145
int mOldSelected
Definition: listbox.h:286
Color mForegroundSelectedColor2
Definition: listbox.h:285
void mouseReleased(MouseEvent &event)
Definition: listbox.cpp:311
void removeSelectionListener(SelectionListener *const selectionListener)
Definition: listbox.cpp:440
virtual void adjustSize()
Definition: listbox.cpp:374
SelectionListenerList::iterator SelectionListenerIterator
Definition: listbox.h:281
void refocus()
Definition: listbox.cpp:365
void safeDraw(Graphics *const graphics)
Definition: listbox.cpp:289
ListModel * mListModel
Definition: listbox.h:261
int mItemPadding
Definition: listbox.h:290
int mPadding
Definition: listbox.h:287
static float mAlpha
Definition: listbox.h:292
void keyPressed(KeyEvent &event)
Definition: listbox.cpp:246
Skin * mSkin
Definition: listbox.h:291
void postInit()
Definition: listbox.cpp:140
void setSelected(const int selected)
Definition: listbox.cpp:399
void addSelectionListener(SelectionListener *const selectionListener)
Definition: listbox.cpp:435
virtual int getSelectionByMouse(const int y) const
Definition: listbox.cpp:392
ListModel * getListModel() const
Definition: listbox.h:194
Color mForegroundSelectedColor
Definition: listbox.h:284
bool mDistributeMousePressed
Definition: listbox.h:293
virtual std::string getElementAt(int i)=0
virtual int getNumberOfElements()=0
MouseButtonT getButton() const
Definition: mouseevent.h:116
int getY() const
Definition: mouseevent.h:138
int getClickCount() const
Definition: mouseevent.h:147
Definition: rect.h:74
int y
Definition: rect.h:214
int width
Definition: rect.h:219
int height
Definition: rect.h:224
float guiAlpha
Definition: settings.h:131
int getOption(const std::string &name) const
Definition: skin.h:106
int getPadding() const
Definition: skin.h:100
void unload(Skin *const skin)
Definition: theme.cpp:250
static std::string getThemePath()
Definition: theme.h:67
Skin * load(const std::string &filename, const std::string &filename2, const bool full, const std::string &defaultPath)
Definition: theme.cpp:179
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
Definition: widget.h:99
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 showPart(const Rect &rectangle)
Definition: widget.cpp:511
Rect mDimension
Definition: widget.h:1101
FocusHandler * mFocusHandler
Definition: widget.h:1116
bool isFocusable() const
Definition: widget.cpp:199
void addMouseListener(MouseListener *const mouseListener)
Definition: widget.cpp:292
void setHeight(const int height)
Definition: widget.cpp:140
void addKeyListener(KeyListener *const keyListener)
Definition: widget.cpp:272
Font * getFont() const
Definition: widget.cpp:331
bool mMouseConsume
Definition: widget.h:1162
#define FOR_EACH(type, iter, array)
Definition: foreach.h:25
Gui * gui
Definition: gui.cpp:111
InputAction ::T InputActionT
Definition: inputaction.h:717
#define nullptr
Definition: localconsts.h:45
#define A_UNUSED
Definition: localconsts.h:160
#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