1+ // SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <stenzek@gmail.com>
2+ // SPDX-License-Identifier: CC-BY-NC-ND-4.0
3+
4+ #include " togglebutton.h"
5+
6+ #include < QtGui/QKeyEvent>
7+ #include < QtGui/QPainter>
8+ #include < QtGui/QPainterPath>
9+ #include < QtWidgets/QStyleOption>
10+
11+ #include " moc_togglebutton.cpp"
12+
13+ ToggleButton::ToggleButton (QWidget* parent) : QAbstractButton(parent), m_offset_animation(this , " offset" )
14+ {
15+ setCheckable (true );
16+ setCursor (Qt::PointingHandCursor);
17+ setFocusPolicy (Qt::StrongFocus);
18+
19+ m_offset_animation.setDuration (150 );
20+ m_offset_animation.setEasingCurve (QEasingCurve::OutCubic);
21+ }
22+
23+ ToggleButton::~ToggleButton () = default ;
24+
25+ QSize ToggleButton::sizeHint () const
26+ {
27+ return QSize (50 , 25 );
28+ }
29+
30+ void ToggleButton::showEvent (QShowEvent* event)
31+ {
32+ QAbstractButton::showEvent (event);
33+
34+ // Make sure the toggle position matches the current state when first shown
35+ updateTogglePosition ();
36+ }
37+
38+ void ToggleButton::resizeEvent (QResizeEvent* event)
39+ {
40+ QAbstractButton::resizeEvent (event);
41+
42+ // Update position when resized since it depends on widget dimensions
43+ updateTogglePosition ();
44+ }
45+
46+ void ToggleButton::updateTogglePosition ()
47+ {
48+ // Immediately set the toggle to the correct position without animation
49+ if (width () > 0 )
50+ {
51+ m_offset_animation.stop ();
52+ m_offset = isChecked () ? width () - height () : 0 ;
53+ update ();
54+ }
55+ }
56+
57+ void ToggleButton::paintEvent (QPaintEvent* event)
58+ {
59+ Q_UNUSED (event);
60+
61+ QPainter painter (this );
62+ painter.setRenderHint (QPainter::Antialiasing);
63+
64+ QStyleOption opt;
65+ opt.initFrom (this );
66+
67+ // Get colors from the current style
68+ QColor background_color = isChecked () ? opt.palette .highlight ().color () : opt.palette .dark ().color ();
69+ QColor thumb_color = opt.palette .light ().color ();
70+
71+ if (m_hovered || hasFocus ())
72+ {
73+ background_color = background_color.lighter (120 );
74+ }
75+
76+ if (!isEnabled ())
77+ {
78+ background_color = opt.palette .mid ().color ();
79+ thumb_color = opt.palette .midlight ().color ();
80+ }
81+
82+ // Draw background
83+ const int track_width = width () - 2 ;
84+ const int track_height = height () - 2 ;
85+ const int corner_radius = track_height / 2 ;
86+
87+ QPainterPath path;
88+ path.addRoundedRect (1 , 1 , track_width, track_height, corner_radius, corner_radius);
89+
90+ painter.fillPath (path, background_color);
91+
92+ // Draw thumb
93+ const int thumb_size = track_height - 4 ;
94+ const int thumb_x = m_offset + 2 ;
95+ const int thumb_y = 2 ;
96+
97+ QPainterPath thumbPath;
98+ thumbPath.addEllipse (thumb_x, thumb_y, thumb_size, thumb_size);
99+
100+ painter.fillPath (thumbPath, thumb_color);
101+ }
102+
103+ void ToggleButton::enterEvent (QEnterEvent* event)
104+ {
105+ Q_UNUSED (event);
106+ m_hovered = true ;
107+ update ();
108+ }
109+
110+ void ToggleButton::leaveEvent (QEvent* event)
111+ {
112+ Q_UNUSED (event);
113+ m_hovered = false ;
114+ update ();
115+ }
116+
117+ void ToggleButton::checkStateSet ()
118+ {
119+ QAbstractButton::checkStateSet ();
120+ animateToggle (isChecked ());
121+ }
122+
123+ void ToggleButton::animateToggle (bool checked)
124+ {
125+ m_offset_animation.stop ();
126+ m_offset_animation.setStartValue (m_offset);
127+ m_offset_animation.setEndValue (checked ? width () - height () : 0 );
128+ m_offset_animation.start ();
129+ }
130+
131+ void ToggleButton::nextCheckState ()
132+ {
133+ QAbstractButton::nextCheckState ();
134+ animateToggle (isChecked ());
135+ update ();
136+ }
137+
138+ void ToggleButton::keyPressEvent (QKeyEvent* event)
139+ {
140+ if (event->key () == Qt::Key_Space || event->key () == Qt::Key_Return)
141+ {
142+ setChecked (!isChecked ());
143+ event->accept ();
144+ }
145+ else
146+ {
147+ QAbstractButton::keyPressEvent (event);
148+ }
149+ }
150+
151+ int ToggleButton::offset () const
152+ {
153+ return m_offset;
154+ }
155+
156+ void ToggleButton::setOffset (int value)
157+ {
158+ m_offset = value;
159+ update ();
160+ }
0 commit comments