Mana
Loading...
Searching...
No Matches
scrollarea.cpp
Go to the documentation of this file.
1/*
2 * The Mana Client
3 * Copyright (C) 2004-2009 The Mana World Development Team
4 * Copyright (C) 2009-2012 The Mana Developers
5 *
6 * This file is part of The Mana Client.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
23
24#include "graphics.h"
25
26#include "gui/gui.h"
27
28#include <guichan/exception.hpp>
29
34
35ScrollArea::ScrollArea(gcn::Widget *widget):
36 gcn::ScrollArea(widget)
37{
38 init();
39}
40
42{
43 delete getContent();
44}
45
46void ScrollArea::setShowButtons(bool showButtons)
47{
48 mShowButtons = showButtons;
49}
50
52{
53 // Draw background by default
54 setOpaque(true);
55
56 auto theme = gui->getTheme();
57
58 int scrollBarWidth = theme->getSkin(SkinType::ScrollAreaVBar).width;
59 if (scrollBarWidth > 0)
60 setScrollbarWidth(scrollBarWidth);
61
62 auto &scrollAreaSkin = theme->getSkin(SkinType::ScrollArea);
63 setShowButtons(scrollAreaSkin.showButtons);
64
65 if (auto content = getContent())
66 content->setFrameSize(scrollAreaSkin.padding);
67
68 // The base color is only used when rendering a square in the corner where
69 // the scrollbars meet. We disable rendering of this square by setting the
70 // base color to transparent.
71 setBaseColor(gcn::Color(0, 0, 0, 0));
72
73 setUpButtonScrollAmount(5);
74 setDownButtonScrollAmount(5);
75 setLeftButtonScrollAmount(5);
76 setRightButtonScrollAmount(5);
77}
78
80{
81 if (!isVisible())
82 return;
83
84 gcn::ScrollArea::logic();
85 gcn::Widget *content = getContent();
86
87 // When no scrollbar in a certain direction, adapt content size to match
88 // the content dimension exactly.
89 if (content)
90 {
91 if (getHorizontalScrollPolicy() == gcn::ScrollArea::SHOW_NEVER)
92 {
93 content->setWidth(getChildrenArea().width -
94 2 * content->getFrameSize());
95 }
96 if (getVerticalScrollPolicy() == gcn::ScrollArea::SHOW_NEVER)
97 {
98 content->setHeight(getChildrenArea().height -
99 2 * content->getFrameSize());
100 }
101 }
102
103 if (mUpButtonPressed)
104 {
105 setVerticalScrollAmount(getVerticalScrollAmount() -
106 mUpButtonScrollAmount);
107 }
108 else if (mDownButtonPressed)
109 {
110 setVerticalScrollAmount(getVerticalScrollAmount() +
111 mDownButtonScrollAmount);
112 }
113 else if (mLeftButtonPressed)
114 {
115 setHorizontalScrollAmount(getHorizontalScrollAmount() -
116 mLeftButtonScrollAmount);
117 }
118 else if (mRightButtonPressed)
119 {
120 setHorizontalScrollAmount(getHorizontalScrollAmount() +
121 mRightButtonScrollAmount);
122 }
123}
124
125void ScrollArea::draw(gcn::Graphics *graphics)
126{
127 if (getFrameSize() == 0)
129
130 gcn::ScrollArea::draw(graphics);
131}
132
133void ScrollArea::drawFrame(gcn::Graphics *graphics)
134{
135 if (!mOpaque)
136 return;
137
138 const int bs = getFrameSize();
139
140 WidgetState state(this);
141 state.width += bs * 2;
142 state.height += + bs * 2;
143
144 gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollArea, state);
145}
146
148{
149 auto g = static_cast<Graphics*>(graphics);
150 g->pushClipRect(getChildrenArea());
151
152 gcn::ScrollArea::drawChildren(graphics);
153
154 g->popClipRect();
155}
156
157void ScrollArea::setOpaque(bool opaque)
158{
159 mOpaque = opaque;
160
161 auto &skin = gui->getTheme()->getSkin(SkinType::ScrollArea);
162 setFrameSize(mOpaque ? skin.frameSize : 0);
163}
164
166{
167 // background is drawn as part of the frame instead
168}
169
170static void drawButton(gcn::Graphics *graphics,
171 SkinType skinType,
172 bool pressed,
173 const gcn::Rectangle &dim)
174{
175 WidgetState state(dim);
176 if (pressed)
177 state.flags |= STATE_SELECTED;
178
179 gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), skinType, state);
180}
181
183{
184 if (!mShowButtons)
185 return;
186
187 drawButton(graphics, SkinType::ButtonUp, mUpButtonPressed, getUpButtonDimension());
188}
189
191{
192 if (!mShowButtons)
193 return;
194
195 drawButton(graphics, SkinType::ButtonDown, mDownButtonPressed, getDownButtonDimension());
196}
197
199{
200 if (!mShowButtons)
201 return;
202
203 drawButton(graphics, SkinType::ButtonLeft, mLeftButtonPressed, getLeftButtonDimension());
204}
205
207{
208 if (!mShowButtons)
209 return;
210
211 drawButton(graphics, SkinType::ButtonRight, mRightButtonPressed, getRightButtonDimension());
212}
213
214void ScrollArea::drawVBar(gcn::Graphics *graphics)
215{
217 if (mHasMouse && (mX > (getWidth() - getScrollbarWidth())))
218 state.flags |= STATE_HOVERED;
219
220 gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaVBar, state);
221}
222
223void ScrollArea::drawHBar(gcn::Graphics *graphics)
224{
226 if (mHasMouse && (mY > (getHeight() - getScrollbarWidth())))
227 state.flags |= STATE_HOVERED;
228
229 gui->getTheme()->drawSkin(static_cast<Graphics *>(graphics), SkinType::ScrollAreaHBar, state);
230}
231
233{
235 if (state.height == 0)
236 return;
237
238 if (mHasMouse && (mX > (getWidth() - getScrollbarWidth())))
239 state.flags |= STATE_HOVERED;
240
242}
243
245{
247 if (state.width == 0)
248 return;
249
250 if (mHasMouse && (mY > (getHeight() - getScrollbarWidth())))
251 state.flags |= STATE_HOVERED;
252
254}
255
261{
262 int w = getWidth();
263 int h = getHeight();
264
265 mHBarVisible = false;
266 mVBarVisible = false;
267
268 if (!getContent())
269 {
270 mHBarVisible = (mHPolicy == SHOW_ALWAYS);
271 mVBarVisible = (mVPolicy == SHOW_ALWAYS);
272 return;
273 }
274
275 const int contentFrameSize = getContent()->getFrameSize();
276 w -= 2 * contentFrameSize;
277 h -= 2 * contentFrameSize;
278
279 if (mHPolicy == SHOW_AUTO &&
280 mVPolicy == SHOW_AUTO)
281 {
282 if (getContent()->getWidth() <= w
283 && getContent()->getHeight() <= h)
284 {
285 mHBarVisible = false;
286 mVBarVisible = false;
287 }
288
289 if (getContent()->getWidth() > w)
290 {
291 mHBarVisible = true;
292 }
293
294 if ((getContent()->getHeight() > h)
295 || (mHBarVisible && getContent()->getHeight() > h - mScrollbarWidth))
296 {
297 mVBarVisible = true;
298 }
299
300 if (mVBarVisible && getContent()->getWidth() > w - mScrollbarWidth)
301 {
302 mHBarVisible = true;
303 }
304
305 return;
306 }
307
308 switch (mHPolicy)
309 {
310 case SHOW_NEVER:
311 mHBarVisible = false;
312 break;
313
314 case SHOW_ALWAYS:
315 mHBarVisible = true;
316 break;
317
318 case SHOW_AUTO:
319 if (mVPolicy == SHOW_NEVER)
320 {
321 mHBarVisible = getContent()->getWidth() > w;
322 }
323 else // (mVPolicy == SHOW_ALWAYS)
324 {
325 mHBarVisible = getContent()->getWidth() > w - mScrollbarWidth;
326 }
327 break;
328
329 default:
330 throw GCN_EXCEPTION("Horizontal scroll policy invalid.");
331 }
332
333 switch (mVPolicy)
334 {
335 case SHOW_NEVER:
336 mVBarVisible = false;
337 break;
338
339 case SHOW_ALWAYS:
340 mVBarVisible = true;
341 break;
342
343 case SHOW_AUTO:
344 if (mHPolicy == SHOW_NEVER)
345 {
346 mVBarVisible = getContent()->getHeight() > h;
347 }
348 else // (mHPolicy == SHOW_ALWAYS)
349 {
350 mVBarVisible = getContent()->getHeight() > h - mScrollbarWidth;
351 }
352 break;
353 default:
354 throw GCN_EXCEPTION("Vertical scroll policy invalid.");
355 }
356}
357
358void ScrollArea::mouseMoved(gcn::MouseEvent& event)
359{
360 mX = event.getX();
361 mY = event.getY();
362}
363
364void ScrollArea::mouseEntered(gcn::MouseEvent& event)
365{
366 mHasMouse = true;
367}
368
369void ScrollArea::mouseExited(gcn::MouseEvent& event)
370{
371 mHasMouse = false;
372}
373
378void ScrollArea::mousePressed(gcn::MouseEvent &mouseEvent)
379{
380 int x = mouseEvent.getX();
381 int y = mouseEvent.getY();
382
383 if (getUpButtonDimension().isPointInRect(x, y))
384 {
385 setVerticalScrollAmount(getVerticalScrollAmount()
386 - mUpButtonScrollAmount);
387 mUpButtonPressed = true;
388 }
389 else if (getDownButtonDimension().isPointInRect(x, y))
390 {
391 setVerticalScrollAmount(getVerticalScrollAmount()
392 + mDownButtonScrollAmount);
393 mDownButtonPressed = true;
394 }
395 else if (getLeftButtonDimension().isPointInRect(x, y))
396 {
397 setHorizontalScrollAmount(getHorizontalScrollAmount()
398 - mLeftButtonScrollAmount);
399 mLeftButtonPressed = true;
400 }
401 else if (getRightButtonDimension().isPointInRect(x, y))
402 {
403 setHorizontalScrollAmount(getHorizontalScrollAmount()
404 + mRightButtonScrollAmount);
405 mRightButtonPressed = true;
406 }
407 else if (getVerticalMarkerDimension().isPointInRect(x, y))
408 {
409 mIsHorizontalMarkerDragged = false;
410 mIsVerticalMarkerDragged = true;
411
412 mVerticalMarkerDragOffset = y - getVerticalMarkerDimension().y;
413 }
414 else if (getVerticalBarDimension().isPointInRect(x,y))
415 {
416 if (y < getVerticalMarkerDimension().y)
417 {
418 setVerticalScrollAmount(getVerticalScrollAmount()
419 - (int)(getChildrenArea().height * 0.95));
420 }
421 else
422 {
423 setVerticalScrollAmount(getVerticalScrollAmount()
424 + (int)(getChildrenArea().height * 0.95));
425 }
426 }
427 else if (getHorizontalMarkerDimension().isPointInRect(x, y))
428 {
429 mIsHorizontalMarkerDragged = true;
430 mIsVerticalMarkerDragged = false;
431
432 mHorizontalMarkerDragOffset = x - getHorizontalMarkerDimension().x;
433 }
434 else if (getHorizontalBarDimension().isPointInRect(x,y))
435 {
437 {
438 setHorizontalScrollAmount(getHorizontalScrollAmount()
439 - (int)(getChildrenArea().width * 0.95));
440 }
441 else
442 {
443 setHorizontalScrollAmount(getHorizontalScrollAmount()
444 + (int)(getChildrenArea().width * 0.95));
445 }
446 }
447}
448
453void ScrollArea::mouseDragged(gcn::MouseEvent &mouseEvent)
454{
455 if (mIsVerticalMarkerDragged)
456 {
457 int pos = mouseEvent.getY() - getVerticalBarDimension().y - mVerticalMarkerDragOffset;
458 int length = getVerticalMarkerDimension().height;
459
460 gcn::Rectangle barDim = getVerticalBarDimension();
461
462 if ((barDim.height - length) > 0)
463 {
464 setVerticalScrollAmount((getVerticalMaxScroll() * pos)
465 / (barDim.height - length));
466 }
467 else
468 {
469 setVerticalScrollAmount(0);
470 }
471 }
472
473 if (mIsHorizontalMarkerDragged)
474 {
475 int pos = mouseEvent.getX() - getHorizontalBarDimension().x - mHorizontalMarkerDragOffset;
476 int length = getHorizontalMarkerDimension().width;
477
478 gcn::Rectangle barDim = getHorizontalBarDimension();
479
480 if ((barDim.width - length) > 0)
481 {
482 setHorizontalScrollAmount((getHorizontalMaxScroll() * pos)
483 / (barDim.width - length));
484 }
485 else
486 {
487 setHorizontalScrollAmount(0);
488 }
489 }
490
491 mouseEvent.consume();
492}
493
495{
496 if (!mVBarVisible || !mShowButtons)
497 return gcn::Rectangle();
498
499 return gcn::Rectangle(getWidth() - mScrollbarWidth, 0, mScrollbarWidth, mScrollbarWidth);
500}
501
503{
504 if (!mVBarVisible || !mShowButtons)
505 return gcn::Rectangle();
506
507 gcn::Rectangle dim(getWidth() - mScrollbarWidth,
508 getHeight() - mScrollbarWidth,
509 mScrollbarWidth,
510 mScrollbarWidth);
511
512 if (mHBarVisible)
513 dim.y -= mScrollbarWidth;
514
515 return dim;
516}
517
519{
520 if (!mHBarVisible || !mShowButtons)
521 return gcn::Rectangle();
522
523 return gcn::Rectangle(0, getHeight() - mScrollbarWidth, mScrollbarWidth, mScrollbarWidth);
524}
525
527{
528 if (!mHBarVisible || !mShowButtons)
529 return gcn::Rectangle();
530
531 gcn::Rectangle dim(getWidth() - mScrollbarWidth,
532 getHeight() - mScrollbarWidth,
533 mScrollbarWidth,
534 mScrollbarWidth);
535
536 if (mVBarVisible)
537 dim.x -= mScrollbarWidth;
538
539 return dim;
540}
541
543{
544 if (!mVBarVisible)
545 return gcn::Rectangle();
546
547 gcn::Rectangle dim(getWidth() - mScrollbarWidth,
548 getUpButtonDimension().height,
549 mScrollbarWidth,
550 getHeight()
551 - getUpButtonDimension().height
552 - getDownButtonDimension().height);
553
554 if (mHBarVisible)
555 dim.height -= mScrollbarWidth;
556
557 if (dim.height < 0)
558 dim.height = 0;
559
560 return dim;
561}
562
564{
565 if (!mHBarVisible)
566 return gcn::Rectangle();
567
568 gcn::Rectangle dim(getLeftButtonDimension().width,
569 getHeight() - mScrollbarWidth,
570 getWidth()
571 - getLeftButtonDimension().width
572 - getRightButtonDimension().width,
573 mScrollbarWidth);
574
575 if (mVBarVisible)
576 dim.width -= mScrollbarWidth;
577
578 if (dim.width < 0)
579 dim.width = 0;
580
581 return dim;
582}
583
584static void getMarkerValues(int barSize,
585 int maxScroll, int scrollAmount,
586 int contentHeight, int viewHeight,
587 int fixedMarkerSize, int minMarkerSize,
588 int &markerSize, int &markerPos)
589{
590 if (fixedMarkerSize == 0)
591 {
592 if (contentHeight != 0 && contentHeight > viewHeight)
593 markerSize = std::max((barSize * viewHeight) / contentHeight, minMarkerSize);
594 else
595 markerSize = barSize;
596 }
597 else
598 {
599 if (contentHeight > viewHeight)
600 markerSize = fixedMarkerSize;
601 else
602 markerSize = 0;
603 }
604
605 // Hide the marker when it doesn't fit
606 if (markerSize > barSize)
607 markerSize = 0;
608
609 if (maxScroll != 0)
610 markerPos = ((barSize - markerSize) * scrollAmount + maxScroll / 2) / maxScroll;
611 else
612 markerPos = 0;
613}
614
616{
617 if (!mVBarVisible)
618 return gcn::Rectangle();
619
620 auto &markerSkin = gui->getTheme()->getSkin(SkinType::ScrollAreaVMarker);
621 const gcn::Rectangle barDim = getVerticalBarDimension();
622
623 int contentHeight = 0;
624 if (auto content = getContent())
625 contentHeight = content->getHeight() + content->getFrameSize() * 2;
626
627 int length;
628 int pos;
629
630 getMarkerValues(barDim.height,
631 getVerticalMaxScroll(),
632 getVerticalScrollAmount(),
633 contentHeight,
634 getChildrenArea().height,
635 markerSkin.height,
636 mScrollbarWidth,
637 length,
638 pos);
639
640 return gcn::Rectangle(barDim.x, barDim.y + pos, mScrollbarWidth, length);
641}
642
644{
645 if (!mHBarVisible)
646 return gcn::Rectangle();
647
648 auto &markerSkin = gui->getTheme()->getSkin(SkinType::ScrollAreaHMarker);
649 const gcn::Rectangle barDim = getHorizontalBarDimension();
650
651 int contentWidth = 0;
652 if (auto content = getContent())
653 contentWidth = content->getWidth() + content->getFrameSize() * 2;
654
655 int length;
656 int pos;
657
658 getMarkerValues(barDim.width,
659 getHorizontalMaxScroll(),
660 getHorizontalScrollAmount(),
661 contentWidth,
662 getChildrenArea().width,
663 markerSkin.width,
664 mScrollbarWidth,
665 length,
666 pos);
667
668 return gcn::Rectangle(barDim.x + pos, barDim.y, length, mScrollbarWidth);
669}
A central point of control for graphics.
Definition graphics.h:78
void pushClipRect(const gcn::Rectangle &rect)
Definition graphics.cpp:252
Theme * getTheme() const
The global GUI theme.
Definition gui.h:138
A scroll area.
Definition scrollarea.h:38
void drawChildren(gcn::Graphics *graphics) override
Applies clipping to the contents.
void drawBackground(gcn::Graphics *graphics) override
void mouseEntered(gcn::MouseEvent &event) override
Called when the mouse enteres the widget area.
void draw(gcn::Graphics *graphics) override
Overridden to draw the frame if its size is 0.
void mouseDragged(gcn::MouseEvent &mouseEvent) override
Code copied from gcn::ScrollArea::mouseDragged to make it call our custom getVerticalMarkerDimension ...
void setShowButtons(bool showButtons)
Sets whether the scroll bar buttons are shown.
void drawHBar(gcn::Graphics *graphics) override
ScrollArea()
Constructor that takes no content.
void drawFrame(gcn::Graphics *graphics) override
Draws the background and border of the scroll area.
void drawVMarker(gcn::Graphics *graphics) override
void mouseExited(gcn::MouseEvent &event) override
Called when the mouse leaves the widget area.
gcn::Rectangle getDownButtonDimension()
gcn::Rectangle getHorizontalBarDimension()
gcn::Rectangle getVerticalMarkerDimension()
Shadowing these functions from gcn::ScrollArea with versions that supports fixed-size scroll bar mark...
gcn::Rectangle getUpButtonDimension()
Shadowing these functions from gcn::ScrollArea with versions that support hiding the buttons.
void drawUpButton(gcn::Graphics *graphics) override
gcn::Rectangle getHorizontalMarkerDimension()
void logic() override
Logic function optionally adapts width or height of contents.
void init()
Initializes the scroll area.
bool mHasMouse
Definition scrollarea.h:152
~ScrollArea() override
Destructor.
void mouseMoved(gcn::MouseEvent &event) override
Called when the mouse moves in the widget area.
void setOpaque(bool opaque)
Sets whether the widget should draw its background or not.
void drawVBar(gcn::Graphics *graphics) override
void checkPolicies() override
Code copied from gcn::ScrollArea::checkPolicies to make sure it takes the frame size of the content i...
void drawDownButton(gcn::Graphics *graphics) override
void mousePressed(gcn::MouseEvent &mouseEvent) override
Code copied from gcn::ScrollArea::mousePressed to make it call our custom getVerticalMarkerDimension ...
void drawLeftButton(gcn::Graphics *graphics) override
gcn::Rectangle getLeftButtonDimension()
bool mShowButtons
Definition scrollarea.h:153
void drawHMarker(gcn::Graphics *graphics) override
gcn::Rectangle getRightButtonDimension()
gcn::Rectangle getVerticalBarDimension()
void drawRightButton(gcn::Graphics *graphics) override
int width
Definition theme.h:180
const Skin & getSkin(SkinType skinType) const
Definition theme.cpp:434
void drawSkin(Graphics *graphics, SkinType type, const WidgetState &state) const
Definition theme.cpp:373
Graphics * graphics
Definition client.cpp:104
Gui * gui
The GUI system.
Definition gui.cpp:50
uint8_t flags
Definition theme.h:150
int width
Definition theme.h:148
int height
Definition theme.h:149
@ STATE_HOVERED
Definition theme.h:105
@ STATE_SELECTED
Definition theme.h:106
SkinType
Definition theme.h:69
@ ScrollAreaHBar
@ ScrollAreaHMarker
@ ScrollAreaVMarker
@ ScrollAreaVBar