GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/skillrectanglelistbox.h Lines: 0 180 0.0 %
Date: 2017-11-29 Branches: 0 162 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2011-2017  The ManaPlus Developers
4
 *
5
 *  This file is part of The ManaPlus Client.
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU General Public License
18
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
 */
20
21
#ifndef GUI_WIDGETS_SKILLRECTANGLELISTBOX_H
22
#define GUI_WIDGETS_SKILLRECTANGLELISTBOX_H
23
24
#include "const/resources/skill.h"
25
26
#include "dragdrop.h"
27
#include "settings.h"
28
29
#include "gui/skin.h"
30
#include "gui/viewport.h"
31
32
#include "gui/fonts/font.h"
33
34
#include "gui/models/skillmodel.h"
35
36
#include "gui/popups/popupmenu.h"
37
#include "gui/popups/skillpopup.h"
38
39
#include "utils/delete2.h"
40
41
#include "render/graphics.h"
42
43
#include "localconsts.h"
44
45
class SkillModel;
46
47
class SkillRectangleListBox final : public Widget,
48
                                    public MouseListener
49
{
50
    public:
51
        SkillRectangleListBox(const Widget2 *const widget,
52
                              SkillModel *const model) :
53
            Widget(widget),
54
            MouseListener(),
55
            mHighlightColor(getThemeColor(ThemeColorId::HIGHLIGHT)),
56
            mTextColor(getThemeColor(ThemeColorId::TEXT)),
57
            mTextColor2(getThemeColor(ThemeColorId::TEXT_OUTLINE)),
58
            mCooldownColor(getThemeColor(ThemeColorId::SKILL_COOLDOWN)),
59
            mForegroundSelectedColor(getThemeColor(
60
                ThemeColorId::LISTBOX_SELECTED)),
61
            mForegroundSelectedColor2(getThemeColor(
62
                ThemeColorId::LISTBOX_SELECTED_OUTLINE)),
63
            mModel(model),
64
            mSkin(nullptr),
65
            mSelected(-1),
66
            mPadding(2),
67
            mBoxWidth(80),
68
            mBoxHeight(70),
69
            mIconXOffset(24),
70
            mIconYOffset(10),
71
            mTextXOffset(0),
72
            mTextYOffset(44),
73
            mSkillClicked(false)
74
        {
75
            if (theme != nullptr)
76
            {
77
                mSkin = theme->load("skillrectanglelistbox.xml",
78
                    "listbox.xml");
79
            }
80
81
            if (mSkin != nullptr)
82
            {
83
                mPadding = mSkin->getPadding();
84
                mBoxWidth = mSkin->getOption("boxWidth", 80);
85
                mBoxHeight = mSkin->getOption("boxHeight", 70);
86
                mIconXOffset = mSkin->getOption("iconXOffset", 24);
87
                mIconYOffset = mSkin->getOption("iconYOffset", 10);
88
                mTextXOffset = mSkin->getOption("textXOffset", 0);
89
                mTextYOffset = mSkin->getOption("textYOffset", 44);
90
            }
91
            Font *const font = getFont();
92
            int minWidth = font->getWidth("Lvl: 10/10") + mTextXOffset + 2;
93
            int minHeight = font->getHeight() + mTextYOffset + 2;
94
            if (mBoxWidth < minWidth)
95
                mBoxWidth = minWidth;
96
            if (mBoxHeight < minHeight)
97
                mBoxHeight = minHeight;
98
            int maxX = 0;
99
            int maxY = 0;
100
            for (int i = 0;
101
                 i < model->getNumberOfElements();
102
                 ++i)
103
            {
104
                SkillInfo *const e = model->getSkillAt(i);
105
                if (e != nullptr)
106
                {
107
                    if (e->x > maxX)
108
                        maxX = e->x;
109
                    if (e->y > maxY)
110
                        maxY = e->y;
111
                }
112
            }
113
            maxX ++;
114
            maxY ++;
115
            setWidth(maxX * mBoxWidth);
116
            setHeight(maxY * mBoxHeight);
117
            addMouseListener(this);
118
        }
119
120
        A_DELETE_COPY(SkillRectangleListBox)
121
122
        ~SkillRectangleListBox()
123
        {
124
            delete2(mModel)
125
        }
126
127
        SkillInfo *getSelectedInfo() const
128
        {
129
            if (mModel == nullptr)
130
                return nullptr;
131
            const int selected = mSelected;
132
            if (selected < 0 ||
133
                selected > mModel->getNumberOfElements())
134
            {
135
                return nullptr;
136
            }
137
138
            return mModel->getSkillAt(selected);
139
        }
140
141
        void draw(Graphics *const graphics) override final A_NONNULL(2)
142
        {
143
            if (mModel == nullptr)
144
                return;
145
146
            SkillModel *const model = mModel;
147
            updateAlpha();
148
149
            int maxX = 0;
150
            int maxY = 0;
151
            mHighlightColor.a = CAST_S32(mAlpha * 255.0F);
152
            graphics->setColor(mHighlightColor);
153
            Font *const font = getFont();
154
            if (mSelected >= 0)
155
            {
156
                SkillInfo *const e = model->getSkillAt(mSelected);
157
                if (e != nullptr)
158
                {
159
                    const int x = e->x * mBoxWidth + mPadding;
160
                    const int y = e->y * mBoxHeight + mPadding;
161
162
                    graphics->fillRectangle(Rect(x, y,
163
                        mBoxWidth, mBoxHeight));
164
165
                    const int xOffset = (mBoxWidth -
166
                        font->getWidth(e->skillLevel)) / 2;
167
                    font->drawString(graphics,
168
                        mForegroundSelectedColor,
169
                        mForegroundSelectedColor,
170
                        e->skillLevel,
171
                        x + mTextXOffset + xOffset,
172
                        y + mTextYOffset);
173
                }
174
            }
175
176
            // +++ need split drawing icons and text
177
            for (int i = 0;
178
                 i < model->getNumberOfElements();
179
                 ++i)
180
            {
181
                SkillInfo *const e = model->getSkillAt(i);
182
                if (e != nullptr)
183
                {
184
                    if (e->x > maxX)
185
                        maxX = e->x;
186
                    if (e->y > maxY)
187
                        maxY = e->y;
188
                    const SkillData *const data = e->data;
189
                    const int x = e->x * mBoxWidth + mPadding;
190
                    const int y = e->y * mBoxHeight + mPadding;
191
192
                    graphics->drawImage(data->icon,
193
                        x + mIconXOffset,
194
                        y + mIconYOffset);
195
196
                    if (i != mSelected)
197
                    {
198
                        const int width1 = font->getWidth(e->skillLevel);
199
                        const int xOffset = (mBoxWidth -
200
                            width1) / 2;
201
                        font->drawString(graphics,
202
                            mTextColor,
203
                            mTextColor2,
204
                            e->skillLevel,
205
                            x + mTextXOffset + xOffset,
206
                            y + mTextYOffset);
207
                        if (e->skillLevelWidth < 0)
208
                        {
209
                            // Add one for padding
210
                            e->skillLevelWidth = width1 + 1;
211
                        }
212
                    }
213
                    else
214
                    {
215
                        if (e->skillLevelWidth < 0)
216
                        {
217
                            // Add one for padding
218
                            e->skillLevelWidth = font->getWidth(
219
                                e->skillLevel) + 1;
220
                        }
221
                    }
222
                }
223
            }
224
            maxX ++;
225
            maxY ++;
226
            setWidth(maxX * mBoxWidth);
227
            setHeight(maxY * mBoxHeight);
228
        }
229
230
        void safeDraw(Graphics *const graphics) override final A_NONNULL(2)
231
        {
232
            SkillRectangleListBox::draw(graphics);
233
        }
234
235
        const SkillInfo *getSkillByEvent(const MouseEvent &event) const
236
        {
237
            if (mModel == nullptr)
238
                return nullptr;
239
            const int posX = (event.getX() - mPadding) / mBoxWidth;
240
            const int posY = (event.getY() - mPadding) / mBoxHeight;
241
            for (int i = 0;
242
                 i < mModel->getNumberOfElements();
243
                 ++i)
244
            {
245
                SkillInfo *const e = mModel->getSkillAt(i);
246
                if (e != nullptr)
247
                {
248
                    if (posX == e->x && posY == e->y)
249
                        return e;
250
                }
251
            }
252
            return nullptr;
253
        }
254
255
        int getSelectionByEvent(const MouseEvent &event) const
256
        {
257
            if (mModel == nullptr)
258
                return -1;
259
            const int posX = (event.getX() - mPadding) / mBoxWidth;
260
            const int posY = (event.getY() - mPadding) / mBoxHeight;
261
            for (int i = 0;
262
                 i < mModel->getNumberOfElements();
263
                 ++i)
264
            {
265
                SkillInfo *const e = mModel->getSkillAt(i);
266
                if (e != nullptr)
267
                {
268
                    if (posX == e->x && posY == e->y)
269
                        return i;
270
                }
271
            }
272
            return -1;
273
        }
274
275
        void mouseMoved(MouseEvent &event) override final
276
        {
277
            if ((viewport == nullptr) || !dragDrop.isEmpty())
278
                return;
279
280
            const SkillInfo *const skill = getSkillByEvent(event);
281
            if (skill == nullptr)
282
                return;
283
            skillPopup->show(skill,
284
                skill->customSelectedLevel,
285
                skill->customCastType,
286
                skill->customOffsetX,
287
                skill->customOffsetY);
288
            skillPopup->position(viewport->mMouseX,
289
                viewport->mMouseY);
290
        }
291
292
        void mouseDragged(MouseEvent &event) override final
293
        {
294
            if (event.getButton() != MouseButton::LEFT)
295
                return;
296
            setSelected(std::max(0, getSelectionByEvent(event)));
297
298
            if (dragDrop.isEmpty())
299
            {
300
                if (mSkillClicked)
301
                {
302
                    mSkillClicked = false;
303
                    const SkillInfo *const skill = getSkillByEvent(event);
304
                    if (skill == nullptr)
305
                        return;
306
                    dragDrop.dragSkill(skill, DragDropSource::Skills);
307
                    dragDrop.setItem(skill->id + SKILL_MIN_ID);
308
                    dragDrop.setItemData(skill->toDataStr());
309
                }
310
            }
311
        }
312
313
        void mousePressed(MouseEvent &event) override final
314
        {
315
            const MouseButtonT button = event.getButton();
316
            if (button == MouseButton::LEFT ||
317
                button == MouseButton::RIGHT)
318
            {
319
                const SkillInfo *const skill = getSkillByEvent(event);
320
                if (skill == nullptr)
321
                    return;
322
                event.consume();
323
                mSkillClicked = true;
324
                SkillModel *const model = mModel;
325
                if ((model != nullptr) &&
326
                    mSelected >= 0 &&
327
                    model->getSkillAt(mSelected) == skill)
328
                {
329
                    skillPopup->hide();
330
331
                    const int x = skill->x * mBoxWidth + mPadding;
332
                    const int y = skill->y * mBoxHeight + mPadding;
333
                    Font *const font = getFont();
334
                    const int height = font->getHeight();
335
                    const int eventX = event.getX();
336
                    const int eventY = event.getY() - mTextYOffset;
337
                    if (button == MouseButton::LEFT)
338
                    {
339
                        if (eventX >= x + mTextXOffset &&
340
                            eventX <= x + mBoxWidth - mTextXOffset &&
341
                            eventY >= y &&
342
                            eventY <= y + height)
343
                        {
344
                            popupMenu->showSkillLevelPopup(skill);
345
                        }
346
                    }
347
                    else if (button == MouseButton::RIGHT)
348
                    {
349
                        popupMenu->showSkillPopup(skill);
350
                    }
351
                }
352
            }
353
        }
354
355
        void mouseReleased(MouseEvent &event) override final
356
        {
357
            if (event.getButton() == MouseButton::LEFT)
358
            {
359
                setSelected(std::max(0, getSelectionByEvent(event)));
360
                distributeActionEvent();
361
            }
362
        }
363
364
        void mouseExited(MouseEvent &event A_UNUSED) override final
365
        {
366
            skillPopup->hide();
367
        }
368
369
        void updateAlpha()
370
        {
371
            const float alpha = std::max(settings.guiAlpha,
372
                theme->getMinimumOpacity());
373
374
            if (mAlpha != alpha)
375
                mAlpha = alpha;
376
        }
377
378
        void setSelected(const int selected)
379
        {
380
            if (mModel == nullptr)
381
            {
382
                mSelected = -1;
383
            }
384
            else
385
            {
386
                if (selected < 0)
387
                    mSelected = -1;
388
                else if (selected >= mModel->getNumberOfElements())
389
                    mSelected = mModel->getNumberOfElements() - 1;
390
                else
391
                    mSelected = selected;
392
            }
393
        }
394
395
    private:
396
        Color mHighlightColor;
397
        Color mTextColor;
398
        Color mTextColor2;
399
        Color mCooldownColor;
400
        Color mForegroundSelectedColor;
401
        Color mForegroundSelectedColor2;
402
        SkillModel *mModel;
403
        Skin *mSkin;
404
        int mSelected;
405
        int mPadding;
406
        int mBoxWidth;
407
        int mBoxHeight;
408
        int mIconXOffset;
409
        int mIconYOffset;
410
        int mTextXOffset;
411
        int mTextYOffset;
412
        bool mSkillClicked;
413
        static float mAlpha;
414
};
415
416
float SkillRectangleListBox::mAlpha = 1.0;
417
418
#endif  // GUI_WIDGETS_SKILLRECTANGLELISTBOX_H