mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Slightly optimized CreateEditFlags function for restrictions.
This commit is contained in:
parent
0c2f5ed76c
commit
db7a251ceb
2 changed files with 138 additions and 189 deletions
|
@ -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);
|
||||||
|
|
|
@ -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 ¤t,
|
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,
|
||||||
|
|
Loading…
Add table
Reference in a new issue