shadow.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "ui/widgets/shadow.h"
  8. #include "ui/ui_utility.h"
  9. #include "styles/style_widgets.h"
  10. #include "styles/palette.h"
  11. #include <QtGui/QPainter>
  12. #include <QtGui/QtEvents>
  13. namespace Ui {
  14. namespace {
  15. struct CustomImage {
  16. public:
  17. explicit CustomImage(const QImage &image)
  18. : _image(image) {
  19. }
  20. void paint(QPainter &p, int x, int y, int outerw) const {
  21. p.drawImage(x, y, _image);
  22. }
  23. void fill(QPainter &p, QRect rect) const {
  24. p.drawImage(rect, _image);
  25. }
  26. [[nodiscard]] bool empty() const {
  27. return _image.isNull();
  28. }
  29. [[nodiscard]] int width() const {
  30. return _image.width() / style::DevicePixelRatio();
  31. }
  32. [[nodiscard]] int height() const {
  33. return _image.height() / style::DevicePixelRatio();
  34. }
  35. private:
  36. const QImage &_image;
  37. };
  38. struct CustomShadowCorners {
  39. const style::icon &left;
  40. CustomImage topLeft;
  41. const style::icon &top;
  42. CustomImage topRight;
  43. const style::icon &right;
  44. CustomImage bottomRight;
  45. const style::icon &bottom;
  46. CustomImage bottomLeft;
  47. const style::margins &extend;
  48. };
  49. struct CustomShadow {
  50. CustomImage left;
  51. CustomImage topLeft;
  52. CustomImage top;
  53. CustomImage topRight;
  54. CustomImage right;
  55. CustomImage bottomRight;
  56. CustomImage bottom;
  57. CustomImage bottomLeft;
  58. const style::margins &extend;
  59. };
  60. template <typename Shadow>
  61. void ShadowPaint(QPainter &p, const QRect &box, int outerWidth, const Shadow &st, RectParts sides) {
  62. auto left = (sides & RectPart::Left);
  63. auto top = (sides & RectPart::Top);
  64. auto right = (sides & RectPart::Right);
  65. auto bottom = (sides & RectPart::Bottom);
  66. if (left) {
  67. auto from = box.y();
  68. auto to = from + box.height();
  69. if (top && !st.topLeft.empty()) {
  70. st.topLeft.paint(p, box.x() - st.extend.left(), box.y() - st.extend.top(), outerWidth);
  71. from += st.topLeft.height() - st.extend.top();
  72. }
  73. if (bottom && !st.bottomLeft.empty()) {
  74. st.bottomLeft.paint(p, box.x() - st.extend.left(), box.y() + box.height() + st.extend.bottom() - st.bottomLeft.height(), outerWidth);
  75. to -= st.bottomLeft.height() - st.extend.bottom();
  76. }
  77. if (to > from && !st.left.empty()) {
  78. st.left.fill(p, style::rtlrect(box.x() - st.extend.left(), from, st.left.width(), to - from, outerWidth));
  79. }
  80. }
  81. if (right) {
  82. auto from = box.y();
  83. auto to = from + box.height();
  84. if (top && !st.topRight.empty()) {
  85. st.topRight.paint(p, box.x() + box.width() + st.extend.right() - st.topRight.width(), box.y() - st.extend.top(), outerWidth);
  86. from += st.topRight.height() - st.extend.top();
  87. }
  88. if (bottom && !st.bottomRight.empty()) {
  89. st.bottomRight.paint(p, box.x() + box.width() + st.extend.right() - st.bottomRight.width(), box.y() + box.height() + st.extend.bottom() - st.bottomRight.height(), outerWidth);
  90. to -= st.bottomRight.height() - st.extend.bottom();
  91. }
  92. if (to > from && !st.right.empty()) {
  93. st.right.fill(p, style::rtlrect(box.x() + box.width() + st.extend.right() - st.right.width(), from, st.right.width(), to - from, outerWidth));
  94. }
  95. }
  96. if (top && !st.top.empty()) {
  97. auto from = box.x();
  98. auto to = from + box.width();
  99. if (left && !st.topLeft.empty()) from += st.topLeft.width() - st.extend.left();
  100. if (right && !st.topRight.empty()) to -= st.topRight.width() - st.extend.right();
  101. if (to > from) {
  102. st.top.fill(p, style::rtlrect(from, box.y() - st.extend.top(), to - from, st.top.height(), outerWidth));
  103. }
  104. }
  105. if (bottom && !st.bottom.empty()) {
  106. auto from = box.x();
  107. auto to = from + box.width();
  108. if (left && !st.bottomLeft.empty()) from += st.bottomLeft.width() - st.extend.left();
  109. if (right && !st.bottomRight.empty()) to -= st.bottomRight.width() - st.extend.right();
  110. if (to > from) {
  111. st.bottom.fill(p, style::rtlrect(from, box.y() + box.height() + st.extend.bottom() - st.bottom.height(), to - from, st.bottom.height(), outerWidth));
  112. }
  113. }
  114. }
  115. } // namespace
  116. PlainShadow::PlainShadow(QWidget *parent)
  117. : PlainShadow(parent, st::shadowFg) {
  118. }
  119. PlainShadow::PlainShadow(QWidget *parent, style::color color)
  120. : RpWidget(parent)
  121. , _color(color) {
  122. resize(st::lineWidth, st::lineWidth);
  123. }
  124. void PlainShadow::paintEvent(QPaintEvent *e) {
  125. QPainter(this).fillRect(e->rect(), _color);
  126. }
  127. void Shadow::paint(QPainter &p, const QRect &box, int outerWidth, const style::Shadow &st, RectParts sides) {
  128. ShadowPaint<style::Shadow>(p, box, outerWidth, st, sides);
  129. }
  130. void Shadow::paint(
  131. QPainter &p,
  132. const QRect &box,
  133. int outerWidth,
  134. const style::Shadow &st,
  135. const std::array<QImage, 4> &corners,
  136. RectParts sides) {
  137. const auto shadow = CustomShadowCorners{
  138. .left = st.left,
  139. .topLeft = CustomImage(corners[0]),
  140. .top = st.top,
  141. .topRight = CustomImage(corners[2]),
  142. .right = st.right,
  143. .bottomRight = CustomImage(corners[3]),
  144. .bottom = st.bottom,
  145. .bottomLeft = CustomImage(corners[1]),
  146. .extend = st.extend,
  147. };
  148. ShadowPaint<CustomShadowCorners>(p, box, outerWidth, shadow, sides);
  149. }
  150. void Shadow::paint(
  151. QPainter &p,
  152. const QRect &box,
  153. int outerWidth,
  154. const style::Shadow &st,
  155. const std::array<QImage, 4> &sides,
  156. const std::array<QImage, 4> &corners) {
  157. const auto shadow = CustomShadow{
  158. .left = CustomImage(sides[0]),
  159. .topLeft = CustomImage(corners[0]),
  160. .top = CustomImage(sides[1]),
  161. .topRight = CustomImage(corners[2]),
  162. .right = CustomImage(sides[2]),
  163. .bottomRight = CustomImage(corners[3]),
  164. .bottom = CustomImage(sides[3]),
  165. .bottomLeft = CustomImage(corners[1]),
  166. .extend = st.extend,
  167. };
  168. ShadowPaint<CustomShadow>(p, box, outerWidth, shadow, RectPart()
  169. | (sides[0].isNull() ? RectPart() : RectPart::Left)
  170. | (sides[1].isNull() ? RectPart() : RectPart::Top)
  171. | (sides[2].isNull() ? RectPart() : RectPart::Right)
  172. | (sides[3].isNull() ? RectPart() : RectPart::Bottom));
  173. }
  174. QPixmap Shadow::grab(
  175. not_null<TWidget*> target,
  176. const style::Shadow &shadow,
  177. RectParts sides) {
  178. SendPendingMoveResizeEvents(target);
  179. auto rect = target->rect();
  180. auto extend = QMargins(
  181. (sides & RectPart::Left) ? shadow.extend.left() : 0,
  182. (sides & RectPart::Top) ? shadow.extend.top() : 0,
  183. (sides & RectPart::Right) ? shadow.extend.right() : 0,
  184. (sides & RectPart::Bottom) ? shadow.extend.bottom() : 0
  185. );
  186. auto full = QRect(0, 0, extend.left() + rect.width() + extend.right(), extend.top() + rect.height() + extend.bottom());
  187. auto result = QPixmap(full.size() * style::DevicePixelRatio());
  188. result.setDevicePixelRatio(style::DevicePixelRatio());
  189. result.fill(Qt::transparent);
  190. {
  191. QPainter p(&result);
  192. Shadow::paint(p, full.marginsRemoved(extend), full.width(), shadow);
  193. RenderWidget(p, target, QPoint(extend.left(), extend.top()));
  194. }
  195. return result;
  196. }
  197. void Shadow::paintEvent(QPaintEvent *e) {
  198. QPainter p(this);
  199. paint(p, rect().marginsRemoved(_st.extend), width(), _st, _sides);
  200. }
  201. } // namespace Ui