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

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