GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/gui/widgets/skillrectanglelistbox.h Lines: 0 183 0.0 %
Date: 2018-09-09 Branches: 0 168 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2011-2018  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, 255U)),
56
            mTextColor(getThemeColor(ThemeColorId::TEXT, 255U)),
57
            mTextColor2(getThemeColor(ThemeColorId::TEXT_OUTLINE, 255U)),
58
            mCooldownColor(getThemeColor(ThemeColorId::SKILL_COOLDOWN, 255U)),
59
            mForegroundSelectedColor(getThemeColor(
60
                ThemeColorId::LISTBOX_SELECTED, 255U)),
61
            mForegroundSelectedColor2(getThemeColor(
62
                ThemeColorId::LISTBOX_SELECTED_OUTLINE, 255U)),
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
                    true,
80
                    Theme::getThemePath());
81
            }
82
83
            if (mSkin != nullptr)
84
            {
85
                mPadding = mSkin->getPadding();
86
                mBoxWidth = mSkin->getOption("boxWidth", 80);
87
                mBoxHeight = mSkin->getOption("boxHeight", 70);
88
                mIconXOffset = mSkin->getOption("iconXOffset", 24);
89
                mIconYOffset = mSkin->getOption("iconYOffset", 10);
90
                mTextXOffset = mSkin->getOption("textXOffset", 0);
91
                mTextYOffset = mSkin->getOption("textYOffset", 44);
92
            }
93
            Font *const font = getFont();
94
            int minWidth = font->getWidth("Lvl: 10/10") + mTextXOffset + 2;
95
            int minHeight = font->getHeight() + mTextYOffset + 2;
96
            if (mBoxWidth < minWidth)
97
                mBoxWidth = minWidth;
98
            if (mBoxHeight < minHeight)
99
                mBoxHeight = minHeight;
100
            int maxX = 0;
101
            int maxY = 0;
102
            for (int i = 0;
103
                 i < model->getNumberOfElements();
104
                 ++i)
105
            {
106
                SkillInfo *const e = model->getSkillAt(i);
107
                if (e != nullptr)
108
                {
109
                    if (e->x > maxX)
110
                        maxX = e->x;
111
                    if (e->y > maxY)
112
                        maxY = e->y;
113
                }
114
            }
115
            maxX ++;
116
            maxY ++;
117
            setWidth(maxX * mBoxWidth);
118
            setHeight(maxY * mBoxHeight);
119
            addMouseListener(this);
120
        }
121
122
        A_DELETE_COPY(SkillRectangleListBox)
123
124
        ~SkillRectangleListBox() override final
125
        {
126
            delete2(mModel)
127
        }
128
129
        SkillInfo *getSelectedInfo() const
130
        {
131
            if (mModel == nullptr)
132
                return nullptr;
133
            const int selected = mSelected;
134
            if (selected < 0 ||
135
                selected > mModel->getNumberOfElements())
136
            {
137
                return nullptr;
138
            }
139
140
            return mModel->getSkillAt(selected);
141
        }
142
143
        void draw(Graphics *const graphics) override final A_NONNULL(2)
144
        {
145
            if (mModel == nullptr)
146
                return;
147
148
            SkillModel *const model = mModel;
149
            updateAlpha();
150
151
            int maxX = 0;
152
            int maxY = 0;
153
            mHighlightColor.a = CAST_S32(mAlpha * 255.0F);
154
            graphics->setColor(mHighlightColor);
155
            Font *const font = getFont();
156
            if (mSelected >= 0)
157
            {
158
                SkillInfo *const e = model->getSkillAt(mSelected);
159
                if (e != nullptr)
160
                {
161
                    const int x = e->x * mBoxWidth + mPadding;
162
                    const int y = e->y * mBoxHeight + mPadding;
163
164
                    graphics->fillRectangle(Rect(x, y,
165
                        mBoxWidth, mBoxHeight));
166
167
                    const int xOffset = (mBoxWidth -
168
                        font->getWidth(e->skillLevel)) / 2;
169
                    font->drawString(graphics,
170
                        mForegroundSelectedColor,
171
                        mForegroundSelectedColor,
172
                        e->skillLevel,
173
                        x + mTextXOffset + xOffset,
174
                        y + mTextYOffset);
175
                }
176
            }
177
178
            // +++ need split drawing icons and text
179
            for (int i = 0;
180
                 i < model->getNumberOfElements();
181
                 ++i)
182
            {
183
                SkillInfo *const e = model->getSkillAt(i);
184
                if (e != nullptr)
185
                {
186
                    if (e->x > maxX)
187
                        maxX = e->x;
188
                    if (e->y > maxY)
189
                        maxY = e->y;
190
                    const SkillData *const data = e->data;
191
                    const int x = e->x * mBoxWidth + mPadding;
192
                    const int y = e->y * mBoxHeight + mPadding;
193
194
                    graphics->drawImage(data->icon,
195
                        x + mIconXOffset,
196
                        y + mIconYOffset);
197
198
                    if (i != mSelected)
199
                    {
200
                        const int width1 = font->getWidth(e->skillLevel);
201
                        const int xOffset = (mBoxWidth -
202
                            width1) / 2;
203
                        font->drawString(graphics,
204
                            mTextColor,
205
                            mTextColor2,
206
                            e->skillLevel,
207
                            x + mTextXOffset + xOffset,
208
                            y + mTextYOffset);
209
                        if (e->skillLevelWidth < 0)
210
                        {
211
                            // Add one for padding
212
                            e->skillLevelWidth = width1 + 1;
213
                        }
214
                    }
215
                    else
216
                    {
217
                        if (e->skillLevelWidth < 0)
218
                        {
219
                            // Add one for padding
220
                            e->skillLevelWidth = font->getWidth(
221
                                e->skillLevel) + 1;
222
                        }
223
                    }
224
                }
225
            }
226
            maxX ++;
227
            maxY ++;
228
            setWidth(maxX * mBoxWidth);
229
            setHeight(maxY * mBoxHeight);
230
        }
231
232
        void safeDraw(Graphics *const graphics) override final A_NONNULL(2)
233
        {
234
            SkillRectangleListBox::draw(graphics);
235
        }
236
237
        const SkillInfo *getSkillByEvent(const MouseEvent &event) const
238
        {
239
            if (mModel == nullptr)
240
                return nullptr;
241
            const int posX = (event.getX() - mPadding) / mBoxWidth;
242
            const int posY = (event.getY() - mPadding) / mBoxHeight;
243
            for (int i = 0;
244
                 i < mModel->getNumberOfElements();
245
                 ++i)
246
            {
247
                SkillInfo *const e = mModel->getSkillAt(i);
248
                if (e != nullptr)
249
                {
250
                    if (posX == e->x && posY == e->y)
251
                        return e;
252
                }
253
            }
254
            return nullptr;
255
        }
256
257
        int getSelectionByEvent(const MouseEvent &event) const
258
        {
259
            if (mModel == nullptr)
260
                return -1;
261
            const int posX = (event.getX() - mPadding) / mBoxWidth;
262
            const int posY = (event.getY() - mPadding) / mBoxHeight;
263
            for (int i = 0;
264
                 i < mModel->getNumberOfElements();
265
                 ++i)
266
            {
267
                SkillInfo *const e = mModel->getSkillAt(i);
268
                if (e != nullptr)
269
                {
270
                    if (posX == e->x && posY == e->y)
271
                        return i;
272
                }
273
            }
274
            return -1;
275
        }
276
277
        void mouseMoved(MouseEvent &event) override final
278
        {
279
            if ((viewport == nullptr) || !dragDrop.isEmpty())
280
                return;
281
282
            const SkillInfo *const skill = getSkillByEvent(event);
283
            if (skill == nullptr)
284
                return;
285
            skillPopup->show(skill,
286
                skill->customSelectedLevel,
287
                skill->customCastType,
288
                skill->customOffsetX,
289
                skill->customOffsetY);
290
            skillPopup->position(viewport->mMouseX,
291
                viewport->mMouseY);
292
        }
293
294
        void mouseDragged(MouseEvent &event) override final
295
        {
296
            if (event.getButton() != MouseButton::LEFT)
297
                return;
298
            setSelected(std::max(0, getSelectionByEvent(event)));
299
300
            if (dragDrop.isEmpty())
301
            {
302
                if (mSkillClicked)
303
                {
304
                    mSkillClicked = false;
305
                    const SkillInfo *const skill = getSkillByEvent(event);
306
                    if (skill == nullptr)
307
                        return;
308
                    dragDrop.dragSkill(skill, DragDropSource::Skills, 0);
309
                    dragDrop.setItem(skill->id + SKILL_MIN_ID);
310
                    dragDrop.setItemData(skill->toDataStr());
311
                }
312
            }
313
        }
314
315
        void mousePressed(MouseEvent &event) override final
316
        {
317
            const MouseButtonT button = event.getButton();
318
            if (button == MouseButton::LEFT ||
319
                button == MouseButton::RIGHT)
320
            {
321
                const SkillInfo *const skill = getSkillByEvent(event);
322
                if (skill == nullptr)
323
                    return;
324
                event.consume();
325
                mSkillClicked = true;
326
                SkillModel *const model = mModel;
327
                if ((model != nullptr) &&
328
                    mSelected >= 0 &&
329
                    model->getSkillAt(mSelected) == skill)
330
                {
331
                    skillPopup->hide();
332
333
                    const int x = skill->x * mBoxWidth + mPadding;
334
                    const int y = skill->y * mBoxHeight + mPadding;
335
                    Font *const font = getFont();
336
                    const int height = font->getHeight();
337
                    const int eventX = event.getX();
338
                    const int eventY = event.getY() - mTextYOffset;
339
                    if (button == MouseButton::LEFT)
340
                    {
341
                        if (eventX >= x + mTextXOffset &&
342
                            eventX <= x + mBoxWidth - mTextXOffset &&
343
                            eventY >= y &&
344
                            eventY <= y + height)
345
                        {
346
                            popupMenu->showSkillLevelPopup(skill);
347
                        }
348
                    }
349
                    else if (button == MouseButton::RIGHT)
350
                    {
351
                        popupMenu->showSkillPopup(skill);
352
                    }
353
                }
354
            }
355
        }
356
357
        void mouseReleased(MouseEvent &event) override final
358
        {
359
            if (event.getButton() == MouseButton::LEFT)
360
            {
361
                setSelected(std::max(0, getSelectionByEvent(event)));
362
                distributeActionEvent();
363
            }
364
        }
365
366
        void mouseExited(MouseEvent &event A_UNUSED) override final
367
        {
368
            skillPopup->hide();
369
        }
370
371
        void updateAlpha()
372
        {
373
            const float alpha = std::max(settings.guiAlpha,
374
                theme->getMinimumOpacity());
375
376
            if (mAlpha != alpha)
377
                mAlpha = alpha;
378
        }
379
380
        void setSelected(const int selected)
381
        {
382
            if (mModel == nullptr)
383
            {
384
                mSelected = -1;
385
            }
386
            else
387
            {
388
                if (selected < 0)
389
                    mSelected = -1;
390
                else if (selected >= mModel->getNumberOfElements())
391
                    mSelected = mModel->getNumberOfElements() - 1;
392
                else
393
                    mSelected = selected;
394
            }
395
        }
396
397
    private:
398
        Color mHighlightColor;
399
        Color mTextColor;
400
        Color mTextColor2;
401
        Color mCooldownColor;
402
        Color mForegroundSelectedColor;
403
        Color mForegroundSelectedColor2;
404
        SkillModel *mModel;
405
        Skin *mSkin;
406
        int mSelected;
407
        int mPadding;
408
        int mBoxWidth;
409
        int mBoxHeight;
410
        int mIconXOffset;
411
        int mIconYOffset;
412
        int mTextXOffset;
413
        int mTextYOffset;
414
        bool mSkillClicked;
415
        static float mAlpha;
416
};
417
418
float SkillRectangleListBox::mAlpha = 1.0;
419
420
#endif  // GUI_WIDGETS_SKILLRECTANGLELISTBOX_H