Changeset View
Changeset View
Standalone View
Standalone View
kcms/cursortheme/xcursor/previewwidget.cpp
Show All 12 Lines | |||||
13 | * You should have received a copy of the GNU General Public License | 13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program; see the file COPYING. If not, write to | 14 | * along with this program; see the file COPYING. If not, write to | ||
15 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 15 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
16 | * Boston, MA 02110-1301, USA. | 16 | * Boston, MA 02110-1301, USA. | ||
17 | */ | 17 | */ | ||
18 | 18 | | |||
19 | #include <QPainter> | 19 | #include <QPainter> | ||
20 | #include <QMouseEvent> | 20 | #include <QMouseEvent> | ||
21 | #include <QQuickWindow> | ||||
21 | 22 | | |||
22 | #include "previewwidget.h" | 23 | #include "previewwidget.h" | ||
23 | 24 | | |||
24 | #include <QX11Info> | 25 | #include <QX11Info> | ||
25 | #include <X11/Xlib.h> | 26 | #include <X11/Xlib.h> | ||
26 | #include <X11/Xcursor/Xcursor.h> | 27 | #include <X11/Xcursor/Xcursor.h> | ||
27 | #include <xcb/xcb.h> | 28 | #include <xcb/xcb.h> | ||
28 | 29 | | |||
Show All 19 Lines | 38 | { | |||
48 | "size_ver", | 49 | "size_ver", | ||
49 | "size_hor", | 50 | "size_hor", | ||
50 | "size_bdiag", | 51 | "size_bdiag", | ||
51 | "split_v", | 52 | "split_v", | ||
52 | }; | 53 | }; | ||
53 | 54 | | |||
54 | const int numCursors = 9; // The number of cursors from the above list to be previewed | 55 | const int numCursors = 9; // The number of cursors from the above list to be previewed | ||
55 | const int cursorSpacing = 20; // Spacing between preview cursors | 56 | const int cursorSpacing = 20; // Spacing between preview cursors | ||
56 | const int widgetMinWidth = 10; // The minimum width of the preview widget | 57 | const qreal widgetMinWidth = 10; // The minimum width of the preview widget | ||
57 | const int widgetMinHeight = 48; // The minimum height of the preview widget | 58 | const qreal widgetMinHeight = 48; // The minimum height of the preview widget | ||
58 | } | 59 | } | ||
59 | 60 | | |||
60 | 61 | | |||
61 | class PreviewCursor | 62 | class PreviewCursor | ||
62 | { | 63 | { | ||
63 | public: | 64 | public: | ||
64 | PreviewCursor( const CursorTheme *theme, const QString &name, int size ); | 65 | PreviewCursor( const CursorTheme *theme, const QString &name, int size ); | ||
65 | ~PreviewCursor(); | 66 | ~PreviewCursor(); | ||
66 | 67 | | |||
67 | const QPixmap &pixmap() const { return m_pixmap; } | 68 | const QPixmap &pixmap() const { return m_pixmap; } | ||
68 | int width() const { return m_pixmap.width(); } | 69 | int width() const { return m_pixmap.width(); } | ||
69 | int height() const { return m_pixmap.height(); } | 70 | int height() const { return m_pixmap.height(); } | ||
71 | int boundingSize() const { return m_boundingSize; } | ||||
70 | inline QRect rect() const; | 72 | inline QRect rect() const; | ||
71 | void setPosition( const QPoint &p ) { m_pos = p; } | 73 | void setPosition( const QPoint &p ) { m_pos = p; } | ||
72 | void setPosition( int x, int y ) { m_pos = QPoint(x, y); } | 74 | void setPosition( int x, int y ) { m_pos = QPoint(x, y); } | ||
73 | QPoint position() const { return m_pos; } | 75 | QPoint position() const { return m_pos; } | ||
74 | operator const uint32_t () const { return m_cursor; } | 76 | operator const uint32_t () const { return m_cursor; } | ||
75 | operator const QPixmap& () const { return pixmap(); } | 77 | operator const QPixmap& () const { return pixmap(); } | ||
76 | 78 | | |||
77 | private: | 79 | private: | ||
80 | int m_boundingSize; | ||||
78 | QPixmap m_pixmap; | 81 | QPixmap m_pixmap; | ||
79 | uint32_t m_cursor; | 82 | uint32_t m_cursor; | ||
80 | QPoint m_pos; | 83 | QPoint m_pos; | ||
81 | }; | 84 | }; | ||
82 | 85 | | |||
83 | 86 | | |||
84 | PreviewCursor::PreviewCursor(const CursorTheme *theme, const QString &name, int size) | 87 | PreviewCursor::PreviewCursor(const CursorTheme *theme, const QString &name, int size) | ||
88 | : m_boundingSize(size > 0 ? size : theme->defaultCursorSize()) | ||||
85 | { | 89 | { | ||
86 | // Create the preview pixmap | 90 | // Create the preview pixmap | ||
87 | QImage image = theme->loadImage(name, size); | 91 | QImage image = theme->loadImage(name, size); | ||
88 | 92 | | |||
89 | if (image.isNull()) | 93 | if (image.isNull()) | ||
90 | return; | 94 | return; | ||
91 | 95 | | |||
92 | m_pixmap = QPixmap::fromImage(image); | 96 | m_pixmap = QPixmap::fromImage(image); | ||
93 | 97 | | |||
94 | // Load the cursor | 98 | // Load the cursor | ||
95 | if (QX11Info::isPlatformX11()) { | | |||
96 | m_cursor = theme->loadCursor(name, size); | 99 | m_cursor = theme->loadCursor(name, size); | ||
97 | } else { | | |||
98 | m_cursor = XCB_CURSOR_NONE; | | |||
99 | } | | |||
100 | // ### perhaps we should tag the cursor so it doesn't get | 100 | // ### perhaps we should tag the cursor so it doesn't get | ||
101 | // replaced when a new theme is applied | 101 | // replaced when a new theme is applied | ||
102 | } | 102 | } | ||
103 | 103 | | |||
104 | PreviewCursor::~PreviewCursor() | 104 | PreviewCursor::~PreviewCursor() | ||
105 | { | 105 | { | ||
106 | if (QX11Info::isPlatformX11() && m_cursor != XCB_CURSOR_NONE) { | 106 | if (QX11Info::isPlatformX11() && m_cursor != XCB_CURSOR_NONE) { | ||
107 | xcb_free_cursor(QX11Info::connection(), m_cursor); | 107 | xcb_free_cursor(QX11Info::connection(), m_cursor); | ||
108 | } | 108 | } | ||
109 | } | 109 | } | ||
110 | 110 | | |||
111 | QRect PreviewCursor::rect() const | 111 | QRect PreviewCursor::rect() const | ||
112 | { | 112 | { | ||
113 | return QRect(m_pos, m_pixmap.size()) | 113 | return QRect(m_pos, m_pixmap.size()) | ||
114 | .adjusted(-(cursorSpacing / 2), -(cursorSpacing / 2), | 114 | .adjusted(-(cursorSpacing / 2), -(cursorSpacing / 2), | ||
115 | cursorSpacing / 2, cursorSpacing / 2); | 115 | cursorSpacing / 2, cursorSpacing / 2); | ||
116 | } | 116 | } | ||
117 | 117 | | |||
118 | 118 | | |||
119 | 119 | | |||
120 | // ------------------------------------------------------------------------------ | 120 | // ------------------------------------------------------------------------------ | ||
121 | 121 | | |||
122 | 122 | | |||
123 | 123 | | |||
124 | PreviewWidget::PreviewWidget(QWidget *parent) : QWidget(parent) | 124 | PreviewWidget::PreviewWidget(QQuickItem *parent) | ||
125 | : QQuickPaintedItem(parent), | ||||
126 | m_currentIndex(-1), | ||||
127 | m_currentSize(0) | ||||
125 | { | 128 | { | ||
126 | setMouseTracking(true); | 129 | setAcceptHoverEvents(true); | ||
127 | current = NULL; | 130 | current = NULL; | ||
128 | } | 131 | } | ||
129 | 132 | | |||
130 | 133 | | |||
131 | PreviewWidget::~PreviewWidget() | 134 | PreviewWidget::~PreviewWidget() | ||
132 | { | 135 | { | ||
133 | qDeleteAll(list); | 136 | qDeleteAll(list); | ||
134 | list.clear(); | 137 | list.clear(); | ||
135 | } | 138 | } | ||
136 | 139 | | |||
140 | void PreviewWidget::setThemeModel(SortProxyModel *themeModel) | ||||
141 | { | ||||
142 | if (m_themeModel == themeModel) { | ||||
143 | return; | ||||
144 | } | ||||
145 | | ||||
146 | m_themeModel = themeModel; | ||||
147 | emit themeModelChanged(); | ||||
148 | } | ||||
149 | | ||||
150 | SortProxyModel *PreviewWidget::themeModel() | ||||
151 | { | ||||
152 | return m_themeModel; | ||||
153 | } | ||||
154 | | ||||
155 | void PreviewWidget::setCurrentIndex(int idx) | ||||
156 | { | ||||
157 | if (m_currentIndex == idx) { | ||||
158 | return; | ||||
159 | } | ||||
160 | | ||||
161 | m_currentIndex = idx; | ||||
162 | emit currentIndexChanged(); | ||||
137 | 163 | | |||
138 | QSize PreviewWidget::sizeHint() const | 164 | if (!m_themeModel) { | ||
165 | return; | ||||
166 | } | ||||
167 | const CursorTheme *theme = m_themeModel->theme(m_themeModel->index(idx, 0)); | ||||
168 | setTheme(theme, m_currentSize); | ||||
169 | } | ||||
170 | | ||||
171 | int PreviewWidget::currentIndex() const | ||||
139 | { | 172 | { | ||
140 | int totalWidth = 0; | 173 | return m_currentIndex; | ||
141 | int maxHeight = 0; | 174 | } | ||
175 | | ||||
176 | void PreviewWidget::setCurrentSize(int size) | ||||
177 | { | ||||
178 | if (m_currentSize == size) { | ||||
179 | return; | ||||
180 | } | ||||
181 | | ||||
182 | m_currentSize = size; | ||||
183 | emit currentSizeChanged(); | ||||
184 | | ||||
185 | if (!m_themeModel) { | ||||
186 | return; | ||||
187 | } | ||||
188 | const CursorTheme *theme = m_themeModel->theme(m_themeModel->index(m_currentIndex, 0)); | ||||
189 | setTheme(theme, size); | ||||
190 | } | ||||
191 | | ||||
192 | int PreviewWidget::currentSize() const | ||||
193 | { | ||||
194 | return m_currentSize; | ||||
195 | } | ||||
196 | | ||||
197 | void PreviewWidget::updateImplicitSize() | ||||
198 | { | ||||
199 | qreal totalWidth = 0; | ||||
200 | qreal maxHeight = 0; | ||||
142 | 201 | | |||
143 | foreach (const PreviewCursor *c, list) | 202 | foreach (const PreviewCursor *c, list) | ||
144 | { | 203 | { | ||
145 | totalWidth += c->width(); | 204 | totalWidth += c->width(); | ||
146 | maxHeight = qMax(c->height(), maxHeight); | 205 | maxHeight = qMax(c->height(), (int)maxHeight); | ||
147 | } | 206 | } | ||
148 | 207 | | |||
149 | totalWidth += (list.count() - 1) * cursorSpacing; | 208 | totalWidth += (list.count() - 1) * cursorSpacing; | ||
150 | maxHeight = qMax(maxHeight, widgetMinHeight); | 209 | maxHeight = qMax(maxHeight, widgetMinHeight); | ||
151 | 210 | | |||
152 | return QSize(qMax(totalWidth, widgetMinWidth), qMax(height(), maxHeight)); | 211 | setImplicitWidth(qMax(totalWidth, widgetMinWidth)); | ||
212 | setImplicitHeight(qMax(height(), maxHeight)); | ||||
153 | } | 213 | } | ||
154 | 214 | | |||
155 | 215 | | |||
156 | void PreviewWidget::layoutItems() | 216 | void PreviewWidget::layoutItems() | ||
157 | { | 217 | { | ||
158 | if (!list.isEmpty()) | 218 | if (!list.isEmpty()) | ||
159 | { | 219 | { | ||
160 | QSize size = sizeHint(); | 220 | const int spacing = 12; | ||
161 | int cursorWidth = size.width() / list.count(); | 221 | int nextX = spacing; | ||
162 | int nextX = (width() - size.width()) / 2; | 222 | int nextY = spacing; | ||
163 | 223 | | |||
164 | foreach (PreviewCursor *c, list) | 224 | foreach (PreviewCursor *c, list) | ||
165 | { | 225 | { | ||
166 | c->setPosition(nextX + (cursorWidth - c->width()) / 2, | 226 | c->setPosition(nextX, nextY); | ||
167 | (height() - c->height()) / 2); | 227 | nextX += c->boundingSize() + spacing; | ||
168 | nextX += cursorWidth; | 228 | if (nextX + c->boundingSize() > width()) { | ||
229 | nextX = spacing; | ||||
230 | nextY += c->boundingSize() + spacing; | ||||
231 | } | ||||
169 | } | 232 | } | ||
170 | } | 233 | } | ||
171 | 234 | | |||
172 | needLayout = false; | 235 | needLayout = false; | ||
173 | } | 236 | } | ||
174 | 237 | | |||
175 | 238 | | |||
176 | void PreviewWidget::setTheme(const CursorTheme *theme, const int size) | 239 | void PreviewWidget::setTheme(const CursorTheme *theme, const int size) | ||
177 | { | 240 | { | ||
178 | qDeleteAll(list); | 241 | qDeleteAll(list); | ||
179 | list.clear(); | 242 | list.clear(); | ||
180 | 243 | | |||
181 | if (theme) | 244 | if (theme) | ||
182 | { | 245 | { | ||
183 | for (int i = 0; i < numCursors; i++) | 246 | for (int i = 0; i < numCursors; i++) | ||
184 | list << new PreviewCursor(theme, cursor_names[i], size); | 247 | list << new PreviewCursor(theme, cursor_names[i], size); | ||
185 | 248 | | |||
186 | needLayout = true; | 249 | needLayout = true; | ||
187 | updateGeometry(); | 250 | updateImplicitSize(); | ||
188 | } | 251 | } | ||
189 | 252 | | |||
190 | current = NULL; | 253 | current = NULL; | ||
191 | update(); | 254 | update(); | ||
192 | } | 255 | } | ||
193 | 256 | | |||
194 | 257 | | |||
195 | void PreviewWidget::paintEvent(QPaintEvent *) | 258 | void PreviewWidget::paint(QPainter *painter) | ||
196 | { | 259 | { | ||
197 | QPainter p(this); | | |||
198 | | ||||
199 | if (needLayout) | 260 | if (needLayout) | ||
200 | layoutItems(); | 261 | layoutItems(); | ||
201 | 262 | | |||
202 | foreach(const PreviewCursor *c, list) | 263 | foreach(const PreviewCursor *c, list) | ||
203 | { | 264 | { | ||
204 | if (c->pixmap().isNull()) | 265 | if (c->pixmap().isNull()) | ||
205 | continue; | 266 | continue; | ||
206 | 267 | | |||
207 | p.drawPixmap(c->position(), *c); | 268 | painter->drawPixmap(c->position(), *c); | ||
208 | } | 269 | } | ||
209 | } | 270 | } | ||
210 | 271 | | |||
211 | 272 | | |||
212 | void PreviewWidget::mouseMoveEvent(QMouseEvent *e) | 273 | void PreviewWidget::hoverMoveEvent(QHoverEvent *e) | ||
213 | { | 274 | { | ||
214 | if (needLayout) | 275 | if (needLayout) | ||
215 | layoutItems(); | 276 | layoutItems(); | ||
216 | 277 | //FIXME: we can't find an handle to the actual window | |||
278 | //in the case we are in a QQuickWidget, so we can't do the live preview | ||||
279 | /* | ||||
217 | foreach (const PreviewCursor *c, list) | 280 | foreach (const PreviewCursor *c, list) | ||
218 | { | 281 | { | ||
219 | if (c->rect().contains(e->pos())) | 282 | if (c->rect().contains(e->pos())) | ||
220 | { | 283 | { | ||
221 | if (c != current) | 284 | if (c != current) | ||
222 | { | 285 | { | ||
223 | // TODO: implement for Wayland | | |||
224 | const uint32_t cursor = *c; | 286 | const uint32_t cursor = *c; | ||
225 | if (QX11Info::isPlatformX11() && (cursor != XCB_CURSOR_NONE)) { | 287 | if (QX11Info::isPlatformX11() && (cursor != XCB_CURSOR_NONE) && window()) { | ||
226 | xcb_change_window_attributes(QX11Info::connection(), winId(), XCB_CW_CURSOR, &cursor); | 288 | xcb_change_window_attributes(QX11Info::connection(), window()->winId(), XCB_CW_CURSOR, &cursor); | ||
227 | } | 289 | } | ||
228 | current = c; | 290 | current = c; | ||
229 | } | 291 | } | ||
230 | return; | 292 | return; | ||
231 | } | 293 | } | ||
232 | } | 294 | } | ||
233 | 295 | | |||
234 | setCursor(Qt::ArrowCursor); | 296 | setCursor(Qt::ArrowCursor); | ||
235 | current = NULL; | 297 | current = NULL; | ||
298 | */ | ||||
236 | } | 299 | } | ||
237 | 300 | | |||
301 | void PreviewWidget::hoverLeaveEvent(QHoverEvent *e) | ||||
302 | { | ||||
303 | if (window()) { | ||||
304 | window()->unsetCursor(); | ||||
305 | } | ||||
306 | } | ||||
238 | 307 | | |||
239 | void PreviewWidget::resizeEvent(QResizeEvent *) | 308 | void PreviewWidget::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) | ||
240 | { | 309 | { | ||
241 | if (!list.isEmpty()) | 310 | Q_UNUSED(newGeometry) | ||
311 | Q_UNUSED(oldGeometry) | ||||
312 | if (!list.isEmpty()) { | ||||
242 | needLayout = true; | 313 | needLayout = true; | ||
243 | } | 314 | } | ||
315 | } | ||||
244 | 316 | |