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