Slightly optimized CreateEditFlags function for restrictions.

This commit is contained in:
23rd 2023-02-01 19:34:50 +03:00 committed by John Preston
parent 0c2f5ed76c
commit db7a251ceb
2 changed files with 138 additions and 189 deletions

View file

@ -609,7 +609,7 @@ rightsButton: SettingsButton(defaultSettingsButton) {
toggle: rightsToggle; toggle: rightsToggle;
toggleOver: rightsToggle; toggleOver: rightsToggle;
toggleSkip: 19px; toggleSkip: 20px;
} }
rightsButtonToggleWidth: 70px; rightsButtonToggleWidth: 70px;
rightsDividerMargin: margins(0px, 0px, 0px, 20px); rightsDividerMargin: margins(0px, 0px, 0px, 20px);

View file

@ -229,7 +229,7 @@ ChatRestrictions DisabledByAdminRights(not_null<PeerData*> peer) {
: Flag::ChangeInfo); : Flag::ChangeInfo);
} }
not_null<Ui::SettingsButton*> SendMediaToggle( not_null<Ui::RpWidget*> SendMediaToggle(
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
rpl::producer<int> checkedValue, rpl::producer<int> checkedValue,
int total, int total,
@ -239,12 +239,24 @@ not_null<Ui::SettingsButton*> SendMediaToggle(
const auto &stButton = st::rightsButton; const auto &stButton = st::rightsButton;
const auto button = container->add(object_ptr<Ui::SettingsButton>( const auto button = container->add(object_ptr<Ui::SettingsButton>(
container, container,
rpl::single(QString()), nullptr,
stButton)); stButton));
const auto toggleButton = Ui::CreateChild<Ui::SettingsButton>( const auto toggleButton = Ui::CreateChild<Ui::SettingsButton>(
container.get(), container.get(),
rpl::single(QString()), nullptr,
stButton); stButton);
struct State final {
State(const style::Toggle &st, Fn<void()> c)
: checkView(st, false, c) {
}
Ui::ToggleView checkView;
Ui::Animations::Simple animation;
};
const auto state = button->lifetime().make_state<State>(
stButton.toggle,
[=] { toggleButton->update(); });
const auto checkView = &state->checkView;
{ {
const auto separator = Ui::CreateChild<Ui::RpWidget>(container.get()); const auto separator = Ui::CreateChild<Ui::RpWidget>(container.get());
separator->paintRequest( separator->paintRequest(
@ -269,23 +281,28 @@ not_null<Ui::SettingsButton*> SendMediaToggle(
kLineWidth, kLineWidth,
separatorHeight); separatorHeight);
}, toggleButton->lifetime()); }, toggleButton->lifetime());
const auto checkWidget = Ui::CreateChild<Ui::RpWidget>(toggleButton);
checkWidget->resize(checkView->getSize());
checkWidget->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(checkWidget);
checkView->paint(p, 0, 0, checkWidget->width());
}, checkWidget->lifetime());
toggleButton->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
checkWidget->moveToRight(
stButton.toggleSkip,
(s.height() - checkWidget->height()) / 2);
}, toggleButton->lifetime());
} }
using namespace rpl::mappers;
button->toggleOn(rpl::duplicate(checkedValue) | rpl::map(_1 > 0), true);
toggleButton->toggleOn(button->toggledValue(), true);
button->setToggleLocked(locked.has_value());
toggleButton->setToggleLocked(locked.has_value());
struct State final {
Ui::Animations::Simple animation;
rpl::lifetime finishAnimatingLifetime;
};
const auto state = button->lifetime().make_state<State>();
rpl::duplicate( rpl::duplicate(
checkedValue checkedValue
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=](int count) {
button->finishAnimating(); checkView->setChecked(count > 0, anim::type::normal);
toggleButton->finishAnimating(); }, toggleButton->lifetime());
}, state->finishAnimatingLifetime); checkView->setLocked(locked.has_value());
checkView->finishAnimating();
const auto label = Ui::CreateChild<Ui::FlatLabel>( const auto label = Ui::CreateChild<Ui::FlatLabel>(
button, button,
rpl::combine( rpl::combine(
@ -323,7 +340,7 @@ not_null<Ui::SettingsButton*> SendMediaToggle(
} }
button->sizeValue( button->sizeValue(
) | rpl::start_with_next([=](const QSize &s) { ) | rpl::start_with_next([=](const QSize &s) {
const auto labelLeft = st::rightsButton.padding.left(); const auto labelLeft = stButton.padding.left();
const auto labelRight = s.width() - toggleButton->width(); const auto labelRight = s.width() - toggleButton->width();
label->resizeToWidth(labelRight - labelLeft - arrow->width()); label->resizeToWidth(labelRight - labelLeft - arrow->width());
@ -366,71 +383,13 @@ not_null<Ui::SettingsButton*> SendMediaToggle(
toggleButton->clicks( toggleButton->clicks(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
if (!handleLocked()) { if (!handleLocked()) {
toggleMedia(!button->toggled()); toggleMedia(!checkView->checked());
state->finishAnimatingLifetime.destroy();
} }
}, toggleButton->lifetime()); }, toggleButton->lifetime());
return button; return button;
} }
not_null<Ui::SettingsButton*> AddInnerCheckbox(
not_null<Ui::VerticalLayout*> container,
const QString &text,
bool toggled,
rpl::producer<> toggledChanges) {
class Button final : public Ui::SettingsButton {
public:
using Ui::SettingsButton::SettingsButton;
protected:
void paintEvent(QPaintEvent *e) override {
Painter p(this);
const auto paintOver = (isOver() || isDown()) && !isDisabled();
Ui::SettingsButton::paintBg(p, e->rect(), paintOver);
Ui::SettingsButton::paintRipple(p, 0, 0);
}
};
const auto checkbox = container->add(
object_ptr<Ui::Checkbox>(
container,
text,
toggled,
st::settingsCheckbox),
st::rightsButton.padding);
const auto button = Ui::CreateChild<Button>(
container.get(),
rpl::single(QString()));
button->stackUnder(checkbox);
rpl::combine(
container->widthValue(),
checkbox->geometryValue()
) | rpl::start_with_next([=](int w, const QRect &r) {
button->setGeometry(0, r.y(), w, r.height());
}, button->lifetime());
checkbox->setAttribute(Qt::WA_TransparentForMouseEvents);
std::move(
toggledChanges
) | rpl::start_with_next([=] {
checkbox->setChecked(button->toggled());
}, checkbox->lifetime());
return button;
};
not_null<Ui::SettingsButton*> AddDefaultCheckbox(
not_null<Ui::VerticalLayout*> container,
const QString &text,
bool toggled) {
const auto button = Settings::AddButton(
container,
rpl::single(text),
st::rightsButton);
return button;
};
template < template <
typename Flags, typename Flags,
typename DisabledMessagePairs, typename DisabledMessagePairs,
@ -441,23 +400,19 @@ template <
Flags checked, Flags checked,
const DisabledMessagePairs &disabledMessagePairs, const DisabledMessagePairs &disabledMessagePairs,
const FlagLabelPairs &flagLabelPairs) { const FlagLabelPairs &flagLabelPairs) {
struct FlagCheck final {
QPointer<Ui::SettingsButton> widget;
rpl::event_stream<bool> checkChanges;
};
struct State final { struct State final {
Ui::SlideWrap<Ui::VerticalLayout> *inner = nullptr; std::map<Flags, not_null<Ui::AbstractCheckView*>> checkViews;
std::map<Flags, FlagCheck> checkboxes; std::vector<not_null<Ui::AbstractCheckView*>> mediaChecks;
rpl::event_stream<> anyChanges; rpl::event_stream<> anyChanges;
std::vector<Fn<void(bool)>> mediaToggleCallbacks;
}; };
const auto state = container->lifetime().make_state<State>(); const auto state = container->lifetime().make_state<State>();
const auto mediaRestrictions = MediaRestrictions(); const auto mediaRestrictions = MediaRestrictions();
auto inner = (Ui::VerticalLayout*)(nullptr);
const auto value = [=] { const auto value = [=] {
auto result = Flags(0); auto result = Flags(0);
for (const auto &[flags, checkbox] : state->checkboxes) { for (const auto &[flags, checkView] : state->checkViews) {
if (checkbox.widget->toggled()) { if (checkView->checked()) {
result |= flags; result |= flags;
} else { } else {
result &= ~flags; result &= ~flags;
@ -465,56 +420,15 @@ template <
} }
return result; return result;
}; };
const auto applyDependencies = [=](Ui::AbstractCheckView *view) {
const auto applyDependencies = [=](Ui::SettingsButton *changed) {
static const auto dependencies = Dependencies(Flags()); static const auto dependencies = Dependencies(Flags());
ApplyDependencies(state->checkViews, dependencies, view);
const auto checkAndApply = [&]( };
auto &current, const auto toggleAllMedia = [=](bool toggled) {
auto dependency, for (const auto &checkView : state->mediaChecks) {
bool isChecked) { checkView->setChecked(toggled, anim::type::normal);
for (const auto &checkbox : state->checkboxes) { }
if ((checkbox.first & dependency) applyDependencies(nullptr);
&& (checkbox.second.widget->toggled() == isChecked)) {
current.checkChanges.fire_copy(isChecked);
return true;
}
}
return false;
};
const auto applySomeDependency = [&] {
auto result = false;
for (auto &entry : state->checkboxes) {
if (entry.second.widget.data() == changed) {
continue;
}
auto isChecked = entry.second.widget->toggled();
for (const auto &dependency : dependencies) {
const auto check = isChecked
? dependency.first
: dependency.second;
if (entry.first & check) {
if (checkAndApply(
entry.second,
(isChecked
? dependency.second
: dependency.first),
!isChecked)) {
result = true;
break;
}
}
}
}
return result;
};
const auto maxFixesCount = int(state->checkboxes.size());
for (auto i = 0; i != maxFixesCount; ++i) {
if (!applySomeDependency()) {
break;
}
};
}; };
container->add( container->add(
@ -532,59 +446,102 @@ template <
? std::make_optional(lockedIt->second) ? std::make_optional(lockedIt->second)
: std::nullopt; : std::nullopt;
const auto toggled = ((checked & flags) != 0); const auto toggled = ((checked & flags) != 0);
auto flagCheck = state->checkboxes.emplace(flags, FlagCheck()).first; const auto isMedia = ranges::any_of(
mediaRestrictions,
[&](auto f) { return (flags & f); });
const auto control = [&] { const auto checkView = [&]() -> not_null<Ui::AbstractCheckView*> {
const auto isMedia = ranges::any_of(
mediaRestrictions,
[&](auto f) { return (flags & f); });
if (isMedia) { if (isMedia) {
state->mediaToggleCallbacks.push_back([=](bool v) { if (!inner) {
flagCheck->second.checkChanges.fire_copy(v);
});
if (!state->inner) {
auto wrap = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>( auto wrap = object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container, container,
object_ptr<Ui::VerticalLayout>(container)); object_ptr<Ui::VerticalLayout>(container));
wrap->hide(anim::type::instant); const auto raw = wrap.data();
raw->hide(anim::type::instant);
inner = raw->entity();
SendMediaToggle( SendMediaToggle(
container, container,
rpl::single( rpl::single(
ChatRestrictions(0) 0
) | rpl::then( ) | rpl::then(state->anyChanges.events(
state->anyChanges.events( ) | rpl::map([=]() -> int {
) | rpl::map(value) | rpl::map(NegateRestrictions) return ranges::count_if(
) | rpl::map([=](ChatRestrictions r) -> int { state->mediaChecks,
return (r == ChatRestrictions(0)) [](const auto &v) { return v->checked(); });
? 0 })),
: ranges::count_if(
mediaRestrictions,
[&](auto f) { return !(r & f); });
}),
mediaRestrictions.size(), mediaRestrictions.size(),
wrap.data(), raw,
[=](bool toggled) { toggleAllMedia,
for (auto &c : state->mediaToggleCallbacks) {
c(toggled);
}
},
locked); locked);
state->inner = container->add(std::move(wrap)); container->add(std::move(wrap));
container->widthValue(
) | rpl::start_with_next([=](int w) {
raw->resizeToWidth(w);
}, raw->lifetime());
} }
const auto checkbox = AddInnerCheckbox( const auto checkbox = inner->add(
state->inner->entity(), object_ptr<Ui::Checkbox>(
text, inner,
toggled, text,
state->anyChanges.events()); toggled,
return checkbox; st::settingsCheckbox),
st::rightsButton.padding);
const auto button = Ui::CreateChild<Ui::RippleButton>(
inner,
st::defaultRippleAnimation);
button->stackUnder(checkbox);
rpl::combine(
inner->widthValue(),
checkbox->geometryValue()
) | rpl::start_with_next([=](int w, const QRect &r) {
button->setGeometry(0, r.y(), w, r.height());
}, button->lifetime());
checkbox->setAttribute(Qt::WA_TransparentForMouseEvents);
const auto checkView = checkbox->checkView();
button->setClickedCallback([=] {
checkView->setChecked(
!checkView->checked(),
anim::type::normal);
});
state->mediaChecks.push_back(checkView);
return checkView;
} else { } else {
return AddDefaultCheckbox(container, text, toggled); const auto button = Settings::AddButton(
container,
rpl::single(text),
st::rightsButton);
const auto toggle = Ui::CreateChild<Ui::RpWidget>(
button.get());
auto &lifetime = toggle->lifetime();
const auto checkView = lifetime.make_state<Ui::ToggleView>(
st::rightsButton.toggle,
toggled,
[=] { toggle->update(); });
toggle->resize(checkView->getSize());
toggle->paintRequest(
) | rpl::start_with_next([=] {
auto p = QPainter(toggle);
checkView->paint(p, 0, 0, toggle->width());
}, toggle->lifetime());
button->sizeValue(
) | rpl::start_with_next([=](const QSize &s) {
toggle->moveToRight(
st::rightsButton.toggleSkip,
(s.height() - toggle->height()) / 2);
}, toggle->lifetime());
button->setClickedCallback([=] {
checkView->setChecked(
!checkView->checked(),
anim::type::normal);
});
checkView->setLocked(locked.has_value());
return checkView;
} }
}(); }();
flagCheck->second.widget = Ui::MakeWeak(control); state->checkViews.emplace(flags, checkView);
control->toggleOn(flagCheck->second.checkChanges.events()); checkView->checkedChanges(
control->setToggleLocked(locked.has_value());
control->toggledChanges(
) | rpl::start_with_next([=](bool checked) { ) | rpl::start_with_next([=](bool checked) {
if (locked.has_value()) { if (locked.has_value()) {
if (checked != toggled) { if (checked != toggled) {
@ -592,33 +549,25 @@ template <
.parentOverride = container, .parentOverride = container,
.text = { *locked }, .text = { *locked },
}); });
flagCheck->second.checkChanges.fire_copy(toggled); checkView->setChecked(toggled, anim::type::instant);
} }
} else { } else {
InvokeQueued(control, [=] { InvokeQueued(container, [=] {
applyDependencies(control); applyDependencies(checkView);
state->anyChanges.fire({}); state->anyChanges.fire({});
}); });
} }
}, control->lifetime()); }, container->lifetime());
flagCheck->second.checkChanges.fire_copy(toggled);
}; };
for (const auto &[flags, label] : flagLabelPairs) { for (const auto &[flags, label] : flagLabelPairs) {
addCheckbox(flags, label); addCheckbox(flags, label);
} }
applyDependencies(nullptr); applyDependencies(nullptr);
for (const auto &[flags, checkbox] : state->checkboxes) { for (const auto &[flags, checkView] : state->checkViews) {
checkbox.widget->finishAnimating(); checkView->finishAnimating();
} }
//
container->widthValue(
) | rpl::start_with_next([=](int w) {
state->inner->resizeToWidth(w);
}, state->inner->lifetime());
//
return { return {
nullptr, nullptr,
value, value,