PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/src/gui/widgets/skillrectanglelistbox.h

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