Improve tabbed slider design.

This commit is contained in:
John Preston 2023-08-09 11:48:42 +02:00
parent f3ba8fea57
commit 6441266879
10 changed files with 76 additions and 46 deletions

View File

@ -351,11 +351,7 @@ filtersRemove: IconButton(stickersRemove) {
emojiPanMargins: margins(10px, 10px, 10px, 10px);
emojiTabs: SettingsSlider(defaultTabsSlider) {
height: 43px;
barTop: 40px;
labelTop: 12px;
}
emojiTabs: defaultTabsSlider;
emojiCategoryIconTop: 6px;
emojiPanAnimation: PanelAnimation(defaultPanelAnimation) {

View File

@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unique_qptr.h"
namespace Ui {
class SettingsSlider;
class VerticalLayout;
class SearchFieldController;
} // namespace Ui

View File

@ -10,10 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/event_stream.h>
#include "window/section_widget.h"
namespace Ui {
class SettingsSlider;
} // namespace Ui
namespace Window {
class ConnectionState;
} // namespace Window

View File

@ -15,7 +15,6 @@ enum class SharedMediaType : signed char;
} // namespace Storage
namespace Ui {
class SettingsSlider;
class FadeShadow;
class PlainShadow;
class PopupMenu;

View File

@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "info/media/info_media_list_widget.h"
namespace Ui {
class SettingsSlider;
class VerticalLayout;
class SearchFieldController;
} // namespace Ui

View File

@ -12,7 +12,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unique_qptr.h"
namespace Ui {
class SettingsSlider;
class VerticalLayout;
} // namespace Ui

View File

@ -13,7 +13,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Ui {
DiscreteSlider::DiscreteSlider(QWidget *parent) : RpWidget(parent) {
DiscreteSlider::DiscreteSlider(QWidget *parent, bool snapToLabel)
: RpWidget(parent)
, _snapToLabel(snapToLabel) {
setCursor(style::cur_pointer);
}
@ -80,9 +82,23 @@ void DiscreteSlider::setSections(const std::vector<QString> &labels) {
resizeToWidth(width());
}
int DiscreteSlider::getCurrentActiveLeft() {
const auto left = _sections.empty() ? 0 : _sections[_selected].left;
return _a_left.value(left);
DiscreteSlider::Range DiscreteSlider::getFinalActiveRange() const {
const auto raw = _sections.empty() ? nullptr : &_sections[_selected];
if (!raw) {
return { 0, 0 };
}
const auto width = _snapToLabel
? std::min(raw->width, raw->label.maxWidth())
: raw->width;
return { raw->left + ((raw->width - width) / 2), width };
}
DiscreteSlider::Range DiscreteSlider::getCurrentActiveRange() const {
const auto to = getFinalActiveRange();
return {
int(base::SafeRound(_a_left.value(to.left))),
int(base::SafeRound(_a_width.value(to.width))),
};
}
template <typename Lambda>
@ -138,11 +154,13 @@ void DiscreteSlider::setSelectedSection(int index) {
if (index < 0 || index >= _sections.size()) return;
if (_selected != index) {
auto from = _sections[_selected].left;
const auto from = getFinalActiveRange();
_selected = index;
auto to = _sections[_selected].left;
auto duration = getAnimationDuration();
_a_left.start([this] { update(); }, from, to, duration);
const auto to = getFinalActiveRange();
const auto duration = getAnimationDuration();
const auto updater = [=] { update(); };
_a_left.start(updater, from.left, to.left, duration);
_a_width.start(updater, from.width, to.width, duration);
_callbackAfterMs = crl::now() + duration;
}
}
@ -166,7 +184,7 @@ DiscreteSlider::Section::Section(
SettingsSlider::SettingsSlider(
QWidget *parent,
const style::SettingsSlider &st)
: DiscreteSlider(parent)
: DiscreteSlider(parent, st.barSnapToLabel)
, _st(st) {
if (_st.barRadius > 0) {
_bar.emplace(_st.barRadius, _st.barFg);
@ -299,7 +317,7 @@ void SettingsSlider::paintEvent(QPaintEvent *e) {
Painter p(this);
auto clip = e->rect();
auto activeLeft = getCurrentActiveLeft();
auto range = getCurrentActiveRange();
const auto drawRect = [&](QRect rect, bool active = false) {
const auto &bar = active ? _barActive : _bar;
@ -310,9 +328,14 @@ void SettingsSlider::paintEvent(QPaintEvent *e) {
}
};
enumerateSections([&](Section &section) {
const auto activeWidth = _st.barSnapToLabel
? section.label.maxWidth()
: section.width;
const auto activeLeft = section.left
+ (section.width - activeWidth) / 2;
auto active = 1.
- std::clamp(
qAbs(activeLeft - section.left) / float64(section.width),
qAbs(range.left - activeLeft) / float64(section.width),
0.,
1.);
if (section.ripple) {
@ -322,36 +345,47 @@ void SettingsSlider::paintEvent(QPaintEvent *e) {
section.ripple.reset();
}
}
auto from = section.left, tofill = section.width;
if (activeLeft > from) {
auto fill = qMin(tofill, activeLeft - from);
drawRect(myrtlrect(from, _st.barTop, fill, _st.barStroke));
from += fill;
tofill -= fill;
}
if (activeLeft + section.width > from) {
if (auto fill = qMin(tofill, activeLeft + section.width - from)) {
drawRect(
myrtlrect(from, _st.barTop, fill, _st.barStroke),
true);
if (!_st.barSnapToLabel) {
auto from = activeLeft, tofill = activeWidth;
if (range.left > from) {
auto fill = qMin(tofill, range.left - from);
drawRect(myrtlrect(from, _st.barTop, fill, _st.barStroke));
from += fill;
tofill -= fill;
}
if (range.left + activeWidth > from) {
if (auto fill = qMin(tofill, range.left + activeWidth - from)) {
drawRect(
myrtlrect(from, _st.barTop, fill, _st.barStroke),
true);
from += fill;
tofill -= fill;
}
}
if (tofill) {
drawRect(myrtlrect(from, _st.barTop, tofill, _st.barStroke));
}
}
if (tofill) {
drawRect(myrtlrect(from, _st.barTop, tofill, _st.barStroke));
}
if (myrtlrect(section.left, _st.labelTop, section.width, _st.labelStyle.font->height).intersects(clip)) {
const auto labelLeft = section.left + (section.width - section.label.maxWidth()) / 2;
if (myrtlrect(labelLeft, _st.labelTop, section.label.maxWidth(), _st.labelStyle.font->height).intersects(clip)) {
p.setPen(anim::pen(_st.labelFg, _st.labelFgActive, active));
section.label.drawLeft(
p,
section.left + (section.width - section.label.maxWidth()) / 2,
labelLeft,
_st.labelTop,
section.label.maxWidth(),
width());
}
return true;
});
if (_st.barSnapToLabel) {
const auto add = _st.barStroke / 2;
const auto from = std::max(range.left - add, 0);
const auto till = std::min(range.left + range.width + add, width());
if (from < till) {
drawRect(myrtlrect(from, _st.barTop, till - from, _st.barStroke), true);
}
}
}
} // namespace Ui

View File

@ -18,7 +18,7 @@ class RippleAnimation;
class DiscreteSlider : public RpWidget {
public:
DiscreteSlider(QWidget *parent);
DiscreteSlider(QWidget *parent, bool snapToLabel);
void addSection(const QString &label);
void setSections(const std::vector<QString> &labels);
@ -49,10 +49,15 @@ protected:
Ui::Text::String label;
std::unique_ptr<RippleAnimation> ripple;
};
struct Range {
int left = 0;
int width = 0;
};
int getCurrentActiveLeft();
[[nodiscard]] Range getFinalActiveRange() const;
[[nodiscard]] Range getCurrentActiveRange() const;
int getSectionsCount() const {
[[nodiscard]] int getSectionsCount() const {
return _sections.size();
}
@ -67,6 +72,7 @@ protected:
void stopAnimation() {
_a_left.stop();
_a_width.stop();
}
void setSelectOnPress(bool selectOnPress);
@ -82,12 +88,14 @@ private:
std::vector<Section> _sections;
int _activeIndex = 0;
bool _selectOnPress = true;
bool _snapToLabel = false;
rpl::event_stream<int> _sectionActivated;
int _pressed = -1;
int _selected = 0;
Ui::Animations::Simple _a_left;
Ui::Animations::Simple _a_width;
int _timerId = -1;
crl::time _callbackAfterMs = 0;

@ -1 +1 @@
Subproject commit 5a11029c461416407a423ac9921356fba0088ab6
Subproject commit 2d03abc7de8558ae8862688a226b3d5a817dc466

@ -1 +1 @@
Subproject commit 0f25a9451012c7fba93777a009f3b5dcc0f1ca89
Subproject commit 5f2e0a32b12df1468419b123ac8fb4bb17cf47cc