AlignmentSelector.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /******************************************************************************
  2. Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published by
  5. the Free Software Foundation, either version 2 of the License, or
  6. (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. ******************************************************************************/
  14. #include "AlignmentSelector.hpp"
  15. #include <util/base.h>
  16. #include <QAccessible>
  17. #include <QMouseEvent>
  18. #include <QPainter>
  19. #include <QStyleOptionButton>
  20. AlignmentSelector::AlignmentSelector(QWidget *parent) : QWidget(parent)
  21. {
  22. setFocusPolicy(Qt::StrongFocus);
  23. setMouseTracking(true);
  24. setAttribute(Qt::WA_Hover);
  25. }
  26. QSize AlignmentSelector::sizeHint() const
  27. {
  28. int base = fontMetrics().height() * 2;
  29. return QSize(base, base);
  30. }
  31. QSize AlignmentSelector::minimumSizeHint() const
  32. {
  33. return QSize(16, 16);
  34. }
  35. Qt::Alignment AlignmentSelector::value() const
  36. {
  37. return cellAlignment(selectedCell);
  38. }
  39. int AlignmentSelector::currentIndex() const
  40. {
  41. return selectedCell;
  42. }
  43. void AlignmentSelector::setAlignment(Qt::Alignment value)
  44. {
  45. alignment = value;
  46. }
  47. void AlignmentSelector::setCurrentIndex(int index)
  48. {
  49. selectCell(index);
  50. }
  51. void AlignmentSelector::paintEvent(QPaintEvent *)
  52. {
  53. QPainter painter(this);
  54. QStyle *style = this->style();
  55. int cellW = gridRect().width() / 3;
  56. int cellH = gridRect().height() / 3;
  57. for (int i = 0; i < 9; ++i) {
  58. QRect rect = cellRect(i);
  59. rect = rect.adjusted(0, 0, -1, -1);
  60. QStyleOptionFrame frameOpt;
  61. frameOpt.rect = rect;
  62. frameOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
  63. frameOpt.lineWidth = 1;
  64. frameOpt.midLineWidth = 0;
  65. if (i == hoveredCell) {
  66. frameOpt.state |= QStyle::State_MouseOver;
  67. }
  68. if (i == selectedCell) {
  69. frameOpt.state |= QStyle::State_On;
  70. }
  71. if (i == focusedCell && hasFocus()) {
  72. frameOpt.state |= QStyle::State_HasFocus;
  73. }
  74. QStyleOptionButton radioOpt;
  75. radioOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
  76. radioOpt.rect = rect.adjusted(cellW / 6, cellH / 6, -cellW / 6, -cellH / 6);
  77. if (i == hoveredCell) {
  78. radioOpt.state |= QStyle::State_MouseOver;
  79. }
  80. if (i == selectedCell) {
  81. radioOpt.state |= QStyle::State_On;
  82. }
  83. if (i == focusedCell && hasFocus()) {
  84. radioOpt.state |= QStyle::State_HasFocus;
  85. }
  86. style->drawPrimitive(QStyle::PE_IndicatorRadioButton, &radioOpt, &painter, this);
  87. style->drawPrimitive(QStyle::PE_Frame, &frameOpt, &painter, this);
  88. if (i == focusedCell && hasFocus()) {
  89. QStyleOptionFocusRect focusOpt;
  90. focusOpt.initFrom(this);
  91. focusOpt.rect = rect.adjusted(1, 1, -1, -1);
  92. focusOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
  93. focusOpt.state |= QStyle::State_HasFocus;
  94. style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this);
  95. }
  96. }
  97. }
  98. QRect AlignmentSelector::cellRect(int index) const
  99. {
  100. int col = index % 3;
  101. int row = index / 3;
  102. QRect gridRect = this->gridRect();
  103. int cellW = gridRect.width() / 3;
  104. int cellH = gridRect.height() / 3;
  105. return QRect(col * cellW + gridRect.left(), row * cellH + gridRect.top(), cellW, cellH);
  106. }
  107. Qt::Alignment AlignmentSelector::cellAlignment(int index) const
  108. {
  109. Qt::Alignment hAlign;
  110. Qt::Alignment vAlign;
  111. switch (index % 3) {
  112. case 0:
  113. hAlign = Qt::AlignLeft;
  114. break;
  115. case 1:
  116. hAlign = Qt::AlignHCenter;
  117. break;
  118. case 2:
  119. hAlign = Qt::AlignRight;
  120. break;
  121. }
  122. switch (index / 3) {
  123. case 0:
  124. vAlign = Qt::AlignTop;
  125. break;
  126. case 1:
  127. vAlign = Qt::AlignVCenter;
  128. break;
  129. case 2:
  130. vAlign = Qt::AlignBottom;
  131. break;
  132. }
  133. return hAlign | vAlign;
  134. }
  135. void AlignmentSelector::leaveEvent(QEvent *)
  136. {
  137. hoveredCell = -1;
  138. update();
  139. }
  140. void AlignmentSelector::mouseMoveEvent(QMouseEvent *event)
  141. {
  142. QRect grid = gridRect();
  143. int cellW = grid.width() / 3;
  144. int cellH = grid.height() / 3;
  145. QPoint pos = event->position().toPoint();
  146. if (!grid.contains(pos)) {
  147. hoveredCell = -1;
  148. return;
  149. }
  150. int col = (pos.x() - grid.left()) / cellW;
  151. int row = (pos.y() - grid.top()) / cellH;
  152. int cell = row * 3 + col;
  153. if (hoveredCell != cell) {
  154. hoveredCell = cell;
  155. update();
  156. }
  157. }
  158. void AlignmentSelector::mousePressEvent(QMouseEvent *event)
  159. {
  160. QRect grid = gridRect();
  161. int cellW = grid.width() / 3;
  162. int cellH = grid.height() / 3;
  163. QPoint pos = event->position().toPoint();
  164. if (!grid.contains(pos)) {
  165. return;
  166. }
  167. int col = (pos.x() - grid.left()) / cellW;
  168. int row = (pos.y() - grid.top()) / cellH;
  169. int cell = row * 3 + col;
  170. selectCell(cell);
  171. }
  172. void AlignmentSelector::keyPressEvent(QKeyEvent *event)
  173. {
  174. int moveX = 0;
  175. int moveY = 0;
  176. switch (event->key()) {
  177. case Qt::Key_Left:
  178. moveX = -1;
  179. break;
  180. case Qt::Key_Right:
  181. moveX = 1;
  182. break;
  183. case Qt::Key_Up:
  184. moveY = -1;
  185. break;
  186. case Qt::Key_Down:
  187. moveY = 1;
  188. break;
  189. case Qt::Key_Space:
  190. case Qt::Key_Return:
  191. case Qt::Key_Enter:
  192. selectCell(focusedCell);
  193. return;
  194. default:
  195. QWidget::keyPressEvent(event);
  196. return;
  197. }
  198. moveFocusedCell(moveX, moveY);
  199. }
  200. QRect AlignmentSelector::gridRect() const
  201. {
  202. int side = std::min(width(), height());
  203. int x = 0;
  204. int y = 0;
  205. if (alignment & Qt::AlignHCenter) {
  206. x = (width() - side) / 2;
  207. } else if (alignment & Qt::AlignRight) {
  208. x = width() - side;
  209. }
  210. if (alignment & Qt::AlignVCenter) {
  211. y = (height() - side) / 2;
  212. } else if (alignment & Qt::AlignBottom) {
  213. y = height() - side;
  214. }
  215. return QRect(x, y, side, side);
  216. }
  217. void AlignmentSelector::moveFocusedCell(int moveX, int moveY)
  218. {
  219. int row = focusedCell / 3;
  220. int col = focusedCell % 3;
  221. row = std::clamp(row + moveY, 0, 2);
  222. col = std::clamp(col + moveX, 0, 2);
  223. int newCell = row * 3 + col;
  224. setFocusedCell(newCell);
  225. }
  226. void AlignmentSelector::setFocusedCell(int cell)
  227. {
  228. if (cell != focusedCell) {
  229. focusedCell = cell;
  230. update();
  231. if (AccessibleAlignmentSelector *interface =
  232. dynamic_cast<AccessibleAlignmentSelector *>(QAccessible::queryAccessibleInterface(this))) {
  233. if (QAccessibleInterface *child = interface->child(cell)) {
  234. QAccessibleEvent event(child, QAccessible::Focus);
  235. QAccessible::updateAccessibility(&event);
  236. }
  237. }
  238. }
  239. }
  240. void AlignmentSelector::selectCell(int cell)
  241. {
  242. setFocusedCell(cell);
  243. if (cell != selectedCell) {
  244. selectedCell = cell;
  245. emit valueChanged(cellAlignment(cell));
  246. emit currentIndexChanged(cell);
  247. }
  248. update();
  249. if (AccessibleAlignmentSelector *interface =
  250. dynamic_cast<AccessibleAlignmentSelector *>(QAccessible::queryAccessibleInterface(this))) {
  251. if (QAccessibleInterface *child = interface->child(cell)) {
  252. QAccessible::State state;
  253. state.checked = true;
  254. QAccessibleStateChangeEvent event(child, state);
  255. QAccessible::updateAccessibility(&event);
  256. }
  257. }
  258. }
  259. void AlignmentSelector::focusInEvent(QFocusEvent *)
  260. {
  261. setFocusedCell(selectedCell);
  262. update();
  263. }
  264. void AlignmentSelector::focusOutEvent(QFocusEvent *)
  265. {
  266. hoveredCell = -1;
  267. setFocusedCell(-1);
  268. update();
  269. }