mirror of https://github.com/procxx/kepka.git
Add a Delete/Share menu for unofficial languages.
This commit is contained in:
parent
27d58e1e2a
commit
5163905954
|
@ -21,17 +21,17 @@ void AutoLockBox::prepare() {
|
||||||
auto options = { 60, 300, 3600, 18000 };
|
auto options = { 60, 300, 3600, 18000 };
|
||||||
|
|
||||||
auto group = std::make_shared<Ui::RadiobuttonGroup>(Global::AutoLock());
|
auto group = std::make_shared<Ui::RadiobuttonGroup>(Global::AutoLock());
|
||||||
auto y = st::boxOptionListPadding.top() + st::langsButton.margin.top();
|
auto y = st::boxOptionListPadding.top() + st::autolockButton.margin.top();
|
||||||
auto count = int(options.size());
|
auto count = int(options.size());
|
||||||
_options.reserve(count);
|
_options.reserve(count);
|
||||||
for (auto seconds : options) {
|
for (auto seconds : options) {
|
||||||
_options.emplace_back(this, group, seconds, (seconds % 3600) ? lng_passcode_autolock_minutes(lt_count, seconds / 60) : lng_passcode_autolock_hours(lt_count, seconds / 3600), st::langsButton);
|
_options.emplace_back(this, group, seconds, (seconds % 3600) ? lng_passcode_autolock_minutes(lt_count, seconds / 60) : lng_passcode_autolock_hours(lt_count, seconds / 3600), st::autolockButton);
|
||||||
_options.back()->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
|
_options.back()->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y);
|
||||||
y += _options.back()->heightNoMargins() + st::boxOptionListSkip;
|
y += _options.back()->heightNoMargins() + st::boxOptionListSkip;
|
||||||
}
|
}
|
||||||
group->setChangedCallback([this](int value) { durationChanged(value); });
|
group->setChangedCallback([this](int value) { durationChanged(value); });
|
||||||
|
|
||||||
setDimensions(st::langsWidth, st::boxOptionListPadding.top() + count * _options.back()->heightNoMargins() + (count - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
|
setDimensions(st::autolockWidth, st::boxOptionListPadding.top() + count * _options.back()->heightNoMargins() + (count - 1) * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AutoLockBox::durationChanged(int seconds) {
|
void AutoLockBox::durationChanged(int seconds) {
|
||||||
|
|
|
@ -556,11 +556,15 @@ connectionPasswordInputField: InputField(defaultInputField) {
|
||||||
}
|
}
|
||||||
connectionIPv6Skip: 11px;
|
connectionIPv6Skip: 11px;
|
||||||
|
|
||||||
langsWidth: 256px;
|
autolockWidth: 256px;
|
||||||
langsButton: Checkbox(defaultBoxCheckbox) {
|
autolockButton: Checkbox(defaultBoxCheckbox) {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
langsRadio: Radio(defaultRadio) {
|
||||||
|
bg: boxBg;
|
||||||
|
}
|
||||||
|
|
||||||
backgroundPadding: 10px;
|
backgroundPadding: 10px;
|
||||||
backgroundSize: size(108px, 193px);
|
backgroundSize: size(108px, 193px);
|
||||||
backgroundScroll: ScrollArea(boxLayerScroll) {
|
backgroundScroll: ScrollArea(boxLayerScroll) {
|
||||||
|
|
|
@ -13,10 +13,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/multi_select.h"
|
#include "ui/widgets/multi_select.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/widgets/dropdown_menu.h"
|
||||||
#include "ui/text/text_entity.h"
|
#include "ui/text/text_entity.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
|
#include "ui/toast/toast.h"
|
||||||
#include "ui/text_options.h"
|
#include "ui/text_options.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
|
@ -26,7 +28,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "lang/lang_instance.h"
|
#include "lang/lang_instance.h"
|
||||||
#include "lang/lang_cloud_manager.h"
|
#include "lang/lang_cloud_manager.h"
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_info.h"
|
||||||
#include "styles/style_passport.h"
|
#include "styles/style_passport.h"
|
||||||
|
#include "styles/style_chat_helpers.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -35,20 +39,26 @@ using Languages = Lang::CloudManager::Languages;
|
||||||
|
|
||||||
class Rows : public Ui::RpWidget {
|
class Rows : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
Rows(QWidget *parent, const Languages &data, const QString &chosen);
|
Rows(
|
||||||
|
QWidget *parent,
|
||||||
|
const Languages &data,
|
||||||
|
const QString &chosen,
|
||||||
|
bool areOfficial);
|
||||||
|
|
||||||
void filter(const QString &query);
|
void filter(const QString &query);
|
||||||
|
|
||||||
int count() const;
|
int count() const;
|
||||||
int selected() const;
|
int selected() const;
|
||||||
void setSelected(int selected);
|
void setSelected(int selected);
|
||||||
rpl::producer<int> selections() const;
|
rpl::producer<bool> hasSelection() const;
|
||||||
|
|
||||||
void activateSelected();
|
void activateSelected();
|
||||||
rpl::producer<Language> activations() const;
|
rpl::producer<Language> activations() const;
|
||||||
|
|
||||||
Ui::ScrollToRequest rowScrollRequest(int index) const;
|
Ui::ScrollToRequest rowScrollRequest(int index) const;
|
||||||
|
|
||||||
|
static int DefaultRowHeight();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
@ -66,33 +76,84 @@ private:
|
||||||
int top = 0;
|
int top = 0;
|
||||||
int height = 0;
|
int height = 0;
|
||||||
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
|
mutable std::unique_ptr<Ui::RippleAnimation> ripple;
|
||||||
|
mutable std::unique_ptr<Ui::RippleAnimation> menuToggleRipple;
|
||||||
|
bool menuToggleForceRippled = false;
|
||||||
int titleHeight = 0;
|
int titleHeight = 0;
|
||||||
int descriptionHeight = 0;
|
int descriptionHeight = 0;
|
||||||
QStringList keywords;
|
QStringList keywords;
|
||||||
|
std::unique_ptr<Ui::RadioView> check;
|
||||||
|
bool removed = false;
|
||||||
};
|
};
|
||||||
|
struct RowSelection {
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
void updateSelected(int selected);
|
inline bool operator==(const RowSelection &other) const {
|
||||||
void updatePressed(int pressed);
|
return (index == other.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct MenuSelection {
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
inline bool operator==(const MenuSelection &other) const {
|
||||||
|
return (index == other.index);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using Selection = base::optional_variant<RowSelection, MenuSelection>;
|
||||||
|
|
||||||
|
void updateSelected(Selection selected);
|
||||||
|
void updatePressed(Selection pressed);
|
||||||
Rows::Row &rowByIndex(int index);
|
Rows::Row &rowByIndex(int index);
|
||||||
const Rows::Row &rowByIndex(int index) const;
|
const Rows::Row &rowByIndex(int index) const;
|
||||||
|
Rows::Row &rowBySelection(Selection selected);
|
||||||
|
const Rows::Row &rowBySelection(Selection selected) const;
|
||||||
|
std::unique_ptr<Ui::RippleAnimation> &rippleBySelection(
|
||||||
|
Selection selected);
|
||||||
|
const std::unique_ptr<Ui::RippleAnimation> &rippleBySelection(
|
||||||
|
Selection selected) const;
|
||||||
|
std::unique_ptr<Ui::RippleAnimation> &rippleBySelection(
|
||||||
|
not_null<Row*> row,
|
||||||
|
Selection selected);
|
||||||
|
const std::unique_ptr<Ui::RippleAnimation> &rippleBySelection(
|
||||||
|
not_null<const Row*> row,
|
||||||
|
Selection selected) const;
|
||||||
|
void addRipple(Selection selected, QPoint position);
|
||||||
|
void ensureRippleBySelection(Selection selected);
|
||||||
|
void ensureRippleBySelection(not_null<Row*> row, Selection selected);
|
||||||
|
int indexFromSelection(Selection selected) const;
|
||||||
int countAvailableWidth() const;
|
int countAvailableWidth() const;
|
||||||
int countAvailableWidth(int newWidth) const;
|
int countAvailableWidth(int newWidth) const;
|
||||||
|
QRect menuToggleArea() const;
|
||||||
|
QRect menuToggleArea(not_null<const Row*> row) const;
|
||||||
|
void repaint(Selection selected);
|
||||||
void repaint(int index);
|
void repaint(int index);
|
||||||
void repaint(const Row &row);
|
void repaint(const Row &row);
|
||||||
void repaintChecked(not_null<Row*> row);
|
void repaintChecked(not_null<const Row*> row);
|
||||||
void activateByIndex(int index);
|
void activateByIndex(int index);
|
||||||
|
|
||||||
|
void showMenu(int index);
|
||||||
|
void setForceRippled(not_null<Row*> row, bool rippled);
|
||||||
|
bool canShare(not_null<const Row*> row) const;
|
||||||
|
bool canRemove(not_null<const Row*> row) const;
|
||||||
|
bool hasMenu(not_null<const Row*> row) const;
|
||||||
|
void share(not_null<const Row*> row) const;
|
||||||
|
void remove(not_null<Row*> row);
|
||||||
|
void restore(not_null<Row*> row);
|
||||||
|
|
||||||
std::vector<Row> _rows;
|
std::vector<Row> _rows;
|
||||||
std::vector<not_null<Row*>> _filtered;
|
std::vector<not_null<Row*>> _filtered;
|
||||||
int _selected = -1;
|
Selection _selected;
|
||||||
int _pressed = -1;
|
Selection _pressed;
|
||||||
QString _chosen;
|
QString _chosen;
|
||||||
QStringList _query;
|
QStringList _query;
|
||||||
|
|
||||||
|
bool _areOfficial = false;
|
||||||
bool _mouseSelection = false;
|
bool _mouseSelection = false;
|
||||||
QPoint _globalMousePosition;
|
QPoint _globalMousePosition;
|
||||||
|
base::unique_qptr<Ui::DropdownMenu> _menu;
|
||||||
|
int _menuShownIndex = -1;
|
||||||
|
bool _menuOtherEntered = false;
|
||||||
|
|
||||||
rpl::event_stream<int> _selections;
|
rpl::event_stream<bool> _hasSelection;
|
||||||
rpl::event_stream<Language> _activations;
|
rpl::event_stream<Language> _activations;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -121,9 +182,14 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Rows::Rows(QWidget *parent, const Languages &data, const QString &chosen)
|
Rows::Rows(
|
||||||
|
QWidget *parent,
|
||||||
|
const Languages &data,
|
||||||
|
const QString &chosen,
|
||||||
|
bool areOfficial)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _chosen(chosen) {
|
, _chosen(chosen)
|
||||||
|
, _areOfficial(areOfficial) {
|
||||||
const auto descriptionOptions = TextParseOptions{
|
const auto descriptionOptions = TextParseOptions{
|
||||||
TextParseMultiline,
|
TextParseMultiline,
|
||||||
0,
|
0,
|
||||||
|
@ -134,6 +200,10 @@ Rows::Rows(QWidget *parent, const Languages &data, const QString &chosen)
|
||||||
for (const auto &item : data) {
|
for (const auto &item : data) {
|
||||||
_rows.push_back(Row{ item });
|
_rows.push_back(Row{ item });
|
||||||
auto &row = _rows.back();
|
auto &row = _rows.back();
|
||||||
|
row.check = std::make_unique<Ui::RadioView>(
|
||||||
|
st::langsRadio,
|
||||||
|
(row.data.id == _chosen),
|
||||||
|
[=, row = &row] { repaint(*row); });
|
||||||
row.title.setText(
|
row.title.setText(
|
||||||
st::semiboldTextStyle,
|
st::semiboldTextStyle,
|
||||||
item.nativeName,
|
item.nativeName,
|
||||||
|
@ -152,6 +222,22 @@ Rows::Rows(QWidget *parent, const Languages &data, const QString &chosen)
|
||||||
|
|
||||||
void Rows::mouseMoveEvent(QMouseEvent *e) {
|
void Rows::mouseMoveEvent(QMouseEvent *e) {
|
||||||
const auto position = e->globalPos();
|
const auto position = e->globalPos();
|
||||||
|
if (_menu) {
|
||||||
|
const auto rect = (_menuShownIndex >= 0)
|
||||||
|
? menuToggleArea(&rowByIndex(_menuShownIndex))
|
||||||
|
: QRect();
|
||||||
|
if (rect.contains(e->pos())) {
|
||||||
|
if (!_menuOtherEntered) {
|
||||||
|
_menuOtherEntered = true;
|
||||||
|
_menu->otherEnter();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_menuOtherEntered) {
|
||||||
|
_menuOtherEntered = false;
|
||||||
|
_menu->otherLeave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!_mouseSelection && position == _globalMousePosition) {
|
if (!_mouseSelection && position == _globalMousePosition) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -170,46 +256,238 @@ void Rows::mouseMoveEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}();
|
}();
|
||||||
updateSelected(index);
|
const auto row = (index >= 0) ? &rowByIndex(index) : nullptr;
|
||||||
|
const auto inMenuToggle = (index >= 0 && hasMenu(row))
|
||||||
|
? menuToggleArea(row).contains(e->pos())
|
||||||
|
: false;
|
||||||
|
if (index < 0) {
|
||||||
|
updateSelected({});
|
||||||
|
} else if (inMenuToggle) {
|
||||||
|
updateSelected(MenuSelection{ index });
|
||||||
|
} else if (!row->removed) {
|
||||||
|
updateSelected(RowSelection{ index });
|
||||||
|
} else {
|
||||||
|
updateSelected({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::mousePressEvent(QMouseEvent *e) {
|
void Rows::mousePressEvent(QMouseEvent *e) {
|
||||||
updatePressed(_selected);
|
updatePressed(_selected);
|
||||||
if (_pressed >= 0) {
|
if (_pressed.has_value()) {
|
||||||
auto &row = rowByIndex(_pressed);
|
addRipple(_pressed, e->pos());
|
||||||
if (!row.ripple) {
|
|
||||||
auto mask = Ui::RippleAnimation::rectMask({
|
|
||||||
width(),
|
|
||||||
row.height
|
|
||||||
});
|
|
||||||
row.ripple = std::make_unique<Ui::RippleAnimation>(
|
|
||||||
st::defaultRippleAnimation,
|
|
||||||
std::move(mask),
|
|
||||||
[=, row = &row] { repaintChecked(row); });
|
|
||||||
}
|
|
||||||
row.ripple->add(e->pos() - QPoint(0, row.top));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QRect Rows::menuToggleArea() const {
|
||||||
|
const auto size = st::topBarSearch.width;
|
||||||
|
const auto top = (DefaultRowHeight() - size) / 2;
|
||||||
|
const auto skip = st::boxLayerScroll.width
|
||||||
|
- st::boxLayerScroll.deltax
|
||||||
|
+ top;
|
||||||
|
const auto left = width() - skip - size;
|
||||||
|
return QRect(left, top, size, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRect Rows::menuToggleArea(not_null<const Row*> row) const {
|
||||||
|
return menuToggleArea().translated(0, row->top);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::addRipple(Selection selected, QPoint position) {
|
||||||
|
Expects(selected.has_value());
|
||||||
|
|
||||||
|
ensureRippleBySelection(selected);
|
||||||
|
|
||||||
|
const auto menu = selected.is<MenuSelection>();
|
||||||
|
const auto &row = rowBySelection(selected);
|
||||||
|
const auto menuArea = menuToggleArea(&row);
|
||||||
|
auto &ripple = rippleBySelection(&row, selected);
|
||||||
|
const auto topleft = menu ? menuArea.topLeft() : QPoint(0, row.top);
|
||||||
|
ripple->add(position - topleft);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::ensureRippleBySelection(Selection selected) {
|
||||||
|
ensureRippleBySelection(&rowBySelection(selected), selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::ensureRippleBySelection(not_null<Row*> row, Selection selected) {
|
||||||
|
auto &ripple = rippleBySelection(row, _pressed);
|
||||||
|
if (ripple) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto menu = selected.is<MenuSelection>();
|
||||||
|
const auto menuArea = menuToggleArea(row);
|
||||||
|
auto mask = menu
|
||||||
|
? Ui::RippleAnimation::ellipseMask(menuArea.size())
|
||||||
|
: Ui::RippleAnimation::rectMask({ width(), row->height });
|
||||||
|
ripple = std::make_unique<Ui::RippleAnimation>(
|
||||||
|
st::defaultRippleAnimation,
|
||||||
|
std::move(mask),
|
||||||
|
[=] { repaintChecked(row); });
|
||||||
|
}
|
||||||
|
|
||||||
void Rows::mouseReleaseEvent(QMouseEvent *e) {
|
void Rows::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
const auto pressed = _pressed;
|
const auto pressed = _pressed;
|
||||||
updatePressed(-1);
|
updatePressed({});
|
||||||
if (pressed == _selected && pressed >= 0) {
|
if (pressed == _selected) {
|
||||||
activateByIndex(_selected);
|
pressed.match([&](RowSelection data) {
|
||||||
|
activateByIndex(data.index);
|
||||||
|
}, [&](MenuSelection data) {
|
||||||
|
showMenu(data.index);
|
||||||
|
}, [](std::nullopt_t) {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Rows::canShare(not_null<const Row*> row) const {
|
||||||
|
return !_areOfficial && !row->data.id.startsWith('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rows::canRemove(not_null<const Row*> row) const {
|
||||||
|
return !_areOfficial && !row->check->checked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rows::hasMenu(not_null<const Row*> row) const {
|
||||||
|
return canShare(row) || canRemove(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::share(not_null<const Row*> row) const {
|
||||||
|
const auto link = qsl("https://t.me/setlanguage/") + row->data.id;
|
||||||
|
QApplication::clipboard()->setText(link);
|
||||||
|
Ui::Toast::Show(lang(lng_username_copied));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::remove(not_null<Row*> row) {
|
||||||
|
row->removed = true;
|
||||||
|
Local::removeRecentLanguage(row->data.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::restore(not_null<Row*> row) {
|
||||||
|
row->removed = false;
|
||||||
|
Local::saveRecentLanguages(ranges::view::all(
|
||||||
|
_rows
|
||||||
|
) | ranges::view::filter([](const Row &row) {
|
||||||
|
return !row.removed;
|
||||||
|
}) | ranges::view::transform([](const Row &row) {
|
||||||
|
return row.data;
|
||||||
|
}) | ranges::to_vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::showMenu(int index) {
|
||||||
|
const auto row = &rowByIndex(index);
|
||||||
|
if (_menu || !hasMenu(row)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_menu = base::make_unique_q<Ui::DropdownMenu>(window());
|
||||||
|
const auto weak = _menu.get();
|
||||||
|
_menu->setHiddenCallback([=] {
|
||||||
|
weak->deleteLater();
|
||||||
|
if (_menu == weak) {
|
||||||
|
setForceRippled(row, false);
|
||||||
|
_menuShownIndex = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_menu->setShowStartCallback([=] {
|
||||||
|
if (_menu == weak) {
|
||||||
|
setForceRippled(row, true);
|
||||||
|
_menuShownIndex = index;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
_menu->setHideStartCallback([=] {
|
||||||
|
if (_menu == weak) {
|
||||||
|
setForceRippled(row, false);
|
||||||
|
_menuShownIndex = -1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const auto addAction = [&](
|
||||||
|
const QString &text,
|
||||||
|
Fn<void()> callback) {
|
||||||
|
return _menu->addAction(text, std::move(callback));
|
||||||
|
};
|
||||||
|
const auto id = row->data.id;
|
||||||
|
if (canShare(row)) {
|
||||||
|
addAction(lang(lng_proxy_edit_share), [=] { share(row); });
|
||||||
|
}
|
||||||
|
if (canRemove(row)) {
|
||||||
|
if (row->removed) {
|
||||||
|
addAction(lang(lng_proxy_menu_restore), [=] {
|
||||||
|
restore(row);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
addAction(lang(lng_proxy_menu_delete), [=] {
|
||||||
|
remove(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto toggle = menuToggleArea(row);
|
||||||
|
const auto parentTopLeft = window()->mapToGlobal({ 0, 0 });
|
||||||
|
const auto buttonTopLeft = mapToGlobal(toggle.topLeft());
|
||||||
|
const auto parent = QRect(parentTopLeft, window()->size());
|
||||||
|
const auto button = QRect(buttonTopLeft, toggle.size());
|
||||||
|
const auto bottom = button.y()
|
||||||
|
+ st::proxyDropdownDownPosition.y()
|
||||||
|
+ _menu->height()
|
||||||
|
- parent.y();
|
||||||
|
const auto top = button.y()
|
||||||
|
+ st::proxyDropdownUpPosition.y()
|
||||||
|
- _menu->height()
|
||||||
|
- parent.y();
|
||||||
|
_menuShownIndex = index;
|
||||||
|
_menuOtherEntered = true;
|
||||||
|
if (bottom > parent.height() && top >= 0) {
|
||||||
|
const auto left = button.x()
|
||||||
|
+ button.width()
|
||||||
|
+ st::proxyDropdownUpPosition.x()
|
||||||
|
- _menu->width()
|
||||||
|
- parent.x();
|
||||||
|
_menu->move(left, top);
|
||||||
|
_menu->showAnimated(Ui::PanelAnimation::Origin::BottomRight);
|
||||||
|
} else {
|
||||||
|
const auto left = button.x()
|
||||||
|
+ button.width()
|
||||||
|
+ st::proxyDropdownDownPosition.x()
|
||||||
|
- _menu->width()
|
||||||
|
- parent.x();
|
||||||
|
_menu->move(left, bottom - _menu->height());
|
||||||
|
_menu->showAnimated(Ui::PanelAnimation::Origin::TopRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::setForceRippled(not_null<Row*> row, bool rippled) {
|
||||||
|
if (row->menuToggleForceRippled != rippled) {
|
||||||
|
row->menuToggleForceRippled = rippled;
|
||||||
|
auto &ripple = rippleBySelection(row, MenuSelection{});
|
||||||
|
if (row->menuToggleForceRippled) {
|
||||||
|
ensureRippleBySelection(row, MenuSelection{});
|
||||||
|
if (ripple->empty()) {
|
||||||
|
ripple->addFading();
|
||||||
|
} else {
|
||||||
|
ripple->lastUnstop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ripple) {
|
||||||
|
ripple->lastStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repaint(*row);
|
||||||
|
}
|
||||||
|
|
||||||
void Rows::activateByIndex(int index) {
|
void Rows::activateByIndex(int index) {
|
||||||
_activations.fire_copy(rowByIndex(index).data);
|
_activations.fire_copy(rowByIndex(index).data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::leaveEventHook(QEvent *e) {
|
void Rows::leaveEventHook(QEvent *e) {
|
||||||
updateSelected(-1);
|
updateSelected({});
|
||||||
|
if (_menu && _menuOtherEntered) {
|
||||||
|
_menuOtherEntered = false;
|
||||||
|
_menu->otherLeave();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::filter(const QString &query) {
|
void Rows::filter(const QString &query) {
|
||||||
updateSelected(-1);
|
updateSelected({});
|
||||||
updatePressed(-1);
|
updatePressed({});
|
||||||
|
_menu = nullptr;
|
||||||
|
_menuShownIndex = -1;
|
||||||
|
|
||||||
_query = TextUtilities::PrepareSearchWords(query);
|
_query = TextUtilities::PrepareSearchWords(query);
|
||||||
|
|
||||||
|
@ -254,9 +532,18 @@ int Rows::count() const {
|
||||||
return _query.isEmpty() ? _rows.size() : _filtered.size();
|
return _query.isEmpty() ? _rows.size() : _filtered.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Rows::indexFromSelection(Selection selected) const {
|
||||||
|
return selected.match([&](RowSelection data) {
|
||||||
|
return data.index;
|
||||||
|
}, [&](MenuSelection data) {
|
||||||
|
return data.index;
|
||||||
|
}, [](std::nullopt_t) {
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
int Rows::selected() const {
|
int Rows::selected() const {
|
||||||
const auto limit = count();
|
return indexFromSelection(_selected);
|
||||||
return (_selected >= 0 && _selected < limit) ? _selected : -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::activateSelected() {
|
void Rows::activateSelected() {
|
||||||
|
@ -273,11 +560,22 @@ rpl::producer<Language> Rows::activations() const {
|
||||||
void Rows::setSelected(int selected) {
|
void Rows::setSelected(int selected) {
|
||||||
_mouseSelection = false;
|
_mouseSelection = false;
|
||||||
const auto limit = count();
|
const auto limit = count();
|
||||||
updateSelected((selected >= 0 && selected < limit) ? selected : -1);
|
if (selected >= 0 && selected < limit) {
|
||||||
|
updateSelected(RowSelection{ selected });
|
||||||
|
} else {
|
||||||
|
updateSelected({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<int> Rows::selections() const {
|
rpl::producer<bool> Rows::hasSelection() const {
|
||||||
return _selections.events();
|
return _hasSelection.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Rows::repaint(Selection selected) {
|
||||||
|
selected.match([](std::nullopt_t) {
|
||||||
|
}, [&](const auto &data) {
|
||||||
|
repaint(data.index);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::repaint(int index) {
|
void Rows::repaint(int index) {
|
||||||
|
@ -290,23 +588,26 @@ void Rows::repaint(const Row &row) {
|
||||||
update(0, row.top, width(), row.height);
|
update(0, row.top, width(), row.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::repaintChecked(not_null<Row*> row) {
|
void Rows::repaintChecked(not_null<const Row*> row) {
|
||||||
const auto found = (ranges::find(_filtered, row) != end(_filtered));
|
const auto found = (ranges::find(_filtered, row) != end(_filtered));
|
||||||
if (_query.isEmpty() || found) {
|
if (_query.isEmpty() || found) {
|
||||||
repaint(*row);
|
repaint(*row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::updateSelected(int selected) {
|
void Rows::updateSelected(Selection selected) {
|
||||||
|
const auto changed = (_selected.has_value() != selected.has_value());
|
||||||
repaint(_selected);
|
repaint(_selected);
|
||||||
_selected = selected;
|
_selected = selected;
|
||||||
repaint(_selected);
|
repaint(_selected);
|
||||||
_selections.fire_copy(_selected);
|
if (changed) {
|
||||||
|
_hasSelection.fire(_selected.has_value());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Rows::updatePressed(int pressed) {
|
void Rows::updatePressed(Selection pressed) {
|
||||||
if (_pressed >= 0) {
|
if (_pressed.has_value()) {
|
||||||
if (const auto ripple = rowByIndex(_pressed).ripple.get()) {
|
if (const auto ripple = rippleBySelection(_pressed).get()) {
|
||||||
ripple->lastStop();
|
ripple->lastStop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -325,11 +626,53 @@ const Rows::Row &Rows::rowByIndex(int index) const {
|
||||||
return _query.isEmpty() ? _rows[index] : *_filtered[index];
|
return _query.isEmpty() ? _rows[index] : *_filtered[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rows::Row &Rows::rowBySelection(Selection selected) {
|
||||||
|
return rowByIndex(indexFromSelection(selected));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Rows::Row &Rows::rowBySelection(Selection selected) const {
|
||||||
|
return rowByIndex(indexFromSelection(selected));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
|
||||||
|
Selection selected) {
|
||||||
|
return rippleBySelection(&rowBySelection(selected), selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
|
||||||
|
Selection selected) const {
|
||||||
|
return rippleBySelection(&rowBySelection(selected), selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
|
||||||
|
not_null<Row*> row,
|
||||||
|
Selection selected) {
|
||||||
|
return selected.is<MenuSelection>()
|
||||||
|
? row->menuToggleRipple
|
||||||
|
: row->ripple;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unique_ptr<Ui::RippleAnimation> &Rows::rippleBySelection(
|
||||||
|
not_null<const Row*> row,
|
||||||
|
Selection selected) const {
|
||||||
|
return const_cast<Rows*>(this)->rippleBySelection(
|
||||||
|
const_cast<Row*>(row.get()),
|
||||||
|
selected);
|
||||||
|
}
|
||||||
|
|
||||||
Ui::ScrollToRequest Rows::rowScrollRequest(int index) const {
|
Ui::ScrollToRequest Rows::rowScrollRequest(int index) const {
|
||||||
const auto &row = rowByIndex(index);
|
const auto &row = rowByIndex(index);
|
||||||
return Ui::ScrollToRequest(row.top, row.top + row.height);
|
return Ui::ScrollToRequest(row.top, row.top + row.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Rows::DefaultRowHeight() {
|
||||||
|
return st::passportRowPadding.top()
|
||||||
|
+ st::semiboldFont->height
|
||||||
|
+ st::passportRowSkip
|
||||||
|
+ st::normalFont->height
|
||||||
|
+ st::passportRowPadding.bottom();
|
||||||
|
}
|
||||||
|
|
||||||
int Rows::resizeGetHeight(int newWidth) {
|
int Rows::resizeGetHeight(int newWidth) {
|
||||||
const auto availableWidth = countAvailableWidth(newWidth);
|
const auto availableWidth = countAvailableWidth(newWidth);
|
||||||
auto result = 0;
|
auto result = 0;
|
||||||
|
@ -349,10 +692,12 @@ int Rows::resizeGetHeight(int newWidth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Rows::countAvailableWidth(int newWidth) const {
|
int Rows::countAvailableWidth(int newWidth) const {
|
||||||
|
const auto right = width() - menuToggleArea().x();
|
||||||
return newWidth
|
return newWidth
|
||||||
- st::passportRowPadding.left()
|
- st::passportRowPadding.left()
|
||||||
- st::passportRowPadding.right()
|
- st::langsRadio.diameter
|
||||||
- st::passportRowReadyIcon.width()
|
- st::passportRowPadding.left()
|
||||||
|
- right
|
||||||
- st::passportRowIconSkip;
|
- st::passportRowIconSkip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,8 +711,15 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||||
const auto ms = getms();
|
const auto ms = getms();
|
||||||
const auto clip = e->rect();
|
const auto clip = e->rect();
|
||||||
|
|
||||||
const auto left = st::passportRowPadding.left();
|
const auto checkLeft = st::passportRowPadding.left();
|
||||||
|
const auto left = checkLeft
|
||||||
|
+ st::langsRadio.diameter
|
||||||
|
+ st::passportRowPadding.left();
|
||||||
const auto availableWidth = countAvailableWidth();
|
const auto availableWidth = countAvailableWidth();
|
||||||
|
const auto menu = menuToggleArea();
|
||||||
|
const auto selectedIndex = (_menuShownIndex >= 0)
|
||||||
|
? _menuShownIndex
|
||||||
|
: indexFromSelection(_pressed.has_value() ? _pressed : _selected);
|
||||||
for (auto i = 0, till = count(); i != till; ++i) {
|
for (auto i = 0, till = count(); i != till; ++i) {
|
||||||
const auto &row = rowByIndex(i);
|
const auto &row = rowByIndex(i);
|
||||||
if (row.top + row.height <= clip.y()) {
|
if (row.top + row.height <= clip.y()) {
|
||||||
|
@ -375,11 +727,12 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||||
} else if (row.top >= clip.y() + clip.height()) {
|
} else if (row.top >= clip.y() + clip.height()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
p.setOpacity(row.removed ? st::stickersRowDisabledOpacity : 1.);
|
||||||
p.translate(0, row.top);
|
p.translate(0, row.top);
|
||||||
const auto guard = gsl::finally([&] { p.translate(0, -row.top); });
|
const auto guard = gsl::finally([&] { p.translate(0, -row.top); });
|
||||||
|
|
||||||
const auto selected = (_selected == i);
|
const auto selected = (selectedIndex == i);
|
||||||
if (selected) {
|
if (selected && !row.removed) {
|
||||||
p.fillRect(0, 0, width(), row.height, st::windowBgOver);
|
p.fillRect(0, 0, width(), row.height, st::windowBgOver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,6 +743,9 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto checkTop = (row.height - st::defaultRadio.diameter) / 2;
|
||||||
|
row.check->paint(p, checkLeft, checkTop, width(), ms);
|
||||||
|
|
||||||
auto top = st::passportRowPadding.top();
|
auto top = st::passportRowPadding.top();
|
||||||
|
|
||||||
p.setPen(st::passportRowTitleFg);
|
p.setPen(st::passportRowTitleFg);
|
||||||
|
@ -400,13 +756,23 @@ void Rows::paintEvent(QPaintEvent *e) {
|
||||||
row.description.drawLeft(p, left, top, availableWidth, width());
|
row.description.drawLeft(p, left, top, availableWidth, width());
|
||||||
top += row.descriptionHeight + st::passportRowPadding.bottom();
|
top += row.descriptionHeight + st::passportRowPadding.bottom();
|
||||||
|
|
||||||
if (row.data.id == _chosen) {
|
if (hasMenu(&row)) {
|
||||||
const auto &icon = st::passportRowReadyIcon;
|
p.setOpacity(1.);
|
||||||
icon.paint(
|
if (selected && row.removed) {
|
||||||
p,
|
PainterHighQualityEnabler hq(p);
|
||||||
width() - st::passportRowPadding.right() - icon.width(),
|
p.setPen(Qt::NoPen);
|
||||||
(row.height - icon.height()) / 2,
|
p.setBrush(st::windowBgOver);
|
||||||
width());
|
p.drawEllipse(menu);
|
||||||
|
}
|
||||||
|
if (row.menuToggleRipple) {
|
||||||
|
row.menuToggleRipple->paint(p, menu.x(), menu.y(), width(), ms);
|
||||||
|
if (row.menuToggleRipple->empty()) {
|
||||||
|
row.menuToggleRipple.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(selected
|
||||||
|
? st::topBarMenuToggle.iconOver
|
||||||
|
: st::topBarMenuToggle.icon).paintInCenter(p, menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -435,11 +801,12 @@ void Content::setupContent(
|
||||||
const auto main = container->add(object_ptr<Rows>(
|
const auto main = container->add(object_ptr<Rows>(
|
||||||
container,
|
container,
|
||||||
official,
|
official,
|
||||||
current));
|
current,
|
||||||
|
true));
|
||||||
container->add(object_ptr<Ui::FixedHeightWidget>(
|
container->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
container,
|
container,
|
||||||
st::boxVerticalMargin));
|
st::boxVerticalMargin));
|
||||||
const auto additional = !unofficial.isEmpty()
|
const auto additional = !unofficial.empty()
|
||||||
? content->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
? content->add(object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||||
content,
|
content,
|
||||||
object_ptr<Ui::VerticalLayout>(content)))
|
object_ptr<Ui::VerticalLayout>(content)))
|
||||||
|
@ -459,7 +826,7 @@ void Content::setupContent(
|
||||||
st::passportFormHeaderPadding)
|
st::passportFormHeaderPadding)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
const auto other = inner
|
const auto other = inner
|
||||||
? inner->add(object_ptr<Rows>(inner, unofficial, current))
|
? inner->add(object_ptr<Rows>(inner, unofficial, current, false))
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (inner) {
|
if (inner) {
|
||||||
inner->add(object_ptr<Ui::FixedHeightWidget>(
|
inner->add(object_ptr<Ui::FixedHeightWidget>(
|
||||||
|
@ -493,9 +860,9 @@ void Content::setupContent(
|
||||||
}, divider->lifetime());
|
}, divider->lifetime());
|
||||||
|
|
||||||
const auto excludeSelections = [](Rows *a, Rows *b) {
|
const auto excludeSelections = [](Rows *a, Rows *b) {
|
||||||
a->selections(
|
a->hasSelection(
|
||||||
) | rpl::filter(
|
) | rpl::filter(
|
||||||
_1 >= 0
|
_1
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
b->setSelected(-1);
|
b->setSelected(-1);
|
||||||
}, a->lifetime());
|
}, a->lifetime());
|
||||||
|
@ -616,8 +983,8 @@ void LanguageBox::prepare() {
|
||||||
|
|
||||||
const auto current = Lang::LanguageIdOrDefault(Lang::Current().id());
|
const auto current = Lang::LanguageIdOrDefault(Lang::Current().id());
|
||||||
auto official = Lang::CurrentCloudManager().languageList();
|
auto official = Lang::CurrentCloudManager().languageList();
|
||||||
if (official.isEmpty()) {
|
if (official.empty()) {
|
||||||
official.push_back({ "en", "English", "English" });
|
official.push_back({ "en", {}, {}, "English", "English" });
|
||||||
}
|
}
|
||||||
ranges::stable_partition(official, [&](const Language &language) {
|
ranges::stable_partition(official, [&](const Language &language) {
|
||||||
return (language.id == current);
|
return (language.id == current);
|
||||||
|
@ -635,12 +1002,12 @@ void LanguageBox::prepare() {
|
||||||
ranges::remove_if(
|
ranges::remove_if(
|
||||||
unofficial,
|
unofficial,
|
||||||
foundInOfficial),
|
foundInOfficial),
|
||||||
unofficial.end());
|
end(unofficial));
|
||||||
ranges::stable_partition(unofficial, [&](const Language &language) {
|
ranges::stable_partition(unofficial, [&](const Language &language) {
|
||||||
return (language.id == current);
|
return (language.id == current);
|
||||||
});
|
});
|
||||||
if (official.front().id != current
|
if (official.front().id != current
|
||||||
&& (unofficial.isEmpty() || unofficial.front().id != current)) {
|
&& (unofficial.empty() || unofficial.front().id != current)) {
|
||||||
const auto name = (current == "#custom")
|
const auto name = (current == "#custom")
|
||||||
? "Custom lang pack"
|
? "Custom lang pack"
|
||||||
: lang(lng_language_name);
|
: lang(lng_language_name);
|
||||||
|
@ -659,7 +1026,7 @@ void LanguageBox::prepare() {
|
||||||
object_ptr<Content>(this, official, unofficial),
|
object_ptr<Content>(this, official, unofficial),
|
||||||
st::boxLayerScroll,
|
st::boxLayerScroll,
|
||||||
select->height());
|
select->height());
|
||||||
inner->resizeToWidth(st::langsWidth);
|
inner->resizeToWidth(st::boxWidth);
|
||||||
|
|
||||||
const auto max = lifetime().make_state<int>(0);
|
const auto max = lifetime().make_state<int>(0);
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
|
@ -668,7 +1035,7 @@ void LanguageBox::prepare() {
|
||||||
_1 + _2
|
_1 + _2
|
||||||
) | rpl::start_with_next([=](int height) {
|
) | rpl::start_with_next([=](int height) {
|
||||||
accumulate_max(*max, height);
|
accumulate_max(*max, height);
|
||||||
setDimensions(st::langsWidth, qMin(*max, st::boxMaxListHeight));
|
setDimensions(st::boxWidth, qMin(*max, st::boxMaxListHeight));
|
||||||
}, inner->lifetime());
|
}, inner->lifetime());
|
||||||
|
|
||||||
select->setSubmittedCallback([=](Qt::KeyboardModifiers) {
|
select->setSubmittedCallback([=](Qt::KeyboardModifiers) {
|
||||||
|
@ -721,12 +1088,7 @@ void LanguageBox::keyPressEvent(QKeyEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int LanguageBox::rowsInPage() const {
|
int LanguageBox::rowsInPage() const {
|
||||||
const auto rowHeight = st::passportRowPadding.top()
|
return std::max(height() / Rows::DefaultRowHeight(), 1);
|
||||||
+ st::semiboldFont->height
|
|
||||||
+ st::passportRowSkip
|
|
||||||
+ st::normalFont->height
|
|
||||||
+ st::passportRowPadding.bottom();
|
|
||||||
return std::max(height() / rowHeight, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageBox::setInnerFocus() {
|
void LanguageBox::setInnerFocus() {
|
||||||
|
@ -738,7 +1100,7 @@ not_null<Ui::MultiSelect*> LanguageBox::createMultiSelect() {
|
||||||
this,
|
this,
|
||||||
st::contactsMultiSelect,
|
st::contactsMultiSelect,
|
||||||
langFactory(lng_participant_filter));
|
langFactory(lng_participant_filter));
|
||||||
result->resizeToWidth(st::langsWidth);
|
result->resizeToWidth(st::boxWidth);
|
||||||
result->moveToLeft(0, 0);
|
result->moveToLeft(0, 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -747,7 +1109,7 @@ base::binary_guard LanguageBox::Show() {
|
||||||
auto result = base::binary_guard();
|
auto result = base::binary_guard();
|
||||||
|
|
||||||
const auto manager = Messenger::Instance().langCloudManager();
|
const auto manager = Messenger::Instance().langCloudManager();
|
||||||
if (manager->languageList().isEmpty()) {
|
if (manager->languageList().empty()) {
|
||||||
auto guard = std::make_shared<base::binary_guard>();
|
auto guard = std::make_shared<base::binary_guard>();
|
||||||
std::tie(result, *guard) = base::make_binary_guard();
|
std::tie(result, *guard) = base::make_binary_guard();
|
||||||
auto alive = std::make_shared<std::unique_ptr<base::Subscription>>(
|
auto alive = std::make_shared<std::unique_ptr<base::Subscription>>(
|
||||||
|
|
|
@ -67,7 +67,7 @@ void SelfDestructionBox::showContent() {
|
||||||
_ttlGroup,
|
_ttlGroup,
|
||||||
value,
|
value,
|
||||||
DaysLabel(value),
|
DaysLabel(value),
|
||||||
st::langsButton);
|
st::autolockButton);
|
||||||
button->moveToLeft(st::boxPadding.left(), y);
|
button->moveToLeft(st::boxPadding.left(), y);
|
||||||
y += button->heightNoMargins() + st::boxOptionListSkip;
|
y += button->heightNoMargins() + st::boxOptionListSkip;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,10 +32,10 @@ class CloudManager : public base::has_weak_ptr, private MTP::Sender, private bas
|
||||||
public:
|
public:
|
||||||
CloudManager(Instance &langpack, not_null<MTP::Instance*> mtproto);
|
CloudManager(Instance &langpack, not_null<MTP::Instance*> mtproto);
|
||||||
|
|
||||||
using Languages = QVector<Language>;
|
using Languages = std::vector<Language>;
|
||||||
|
|
||||||
void requestLanguageList();
|
void requestLanguageList();
|
||||||
Languages languageList() const {
|
const Languages &languageList() const {
|
||||||
return _languages;
|
return _languages;
|
||||||
}
|
}
|
||||||
base::Observable<void> &languageListChanged() {
|
base::Observable<void> &languageListChanged() {
|
||||||
|
|
|
@ -4131,17 +4131,15 @@ void writeLangPack() {
|
||||||
file.writeEncrypted(data, SettingsKey);
|
file.writeEncrypted(data, SettingsKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pushRecentLanguage(const Lang::Language &language) {
|
void saveRecentLanguages(const std::vector<Lang::Language> &list) {
|
||||||
if (language.id.startsWith('#')) {
|
if (list.empty()) {
|
||||||
|
if (_languagesKey) {
|
||||||
|
clearKey(_languagesKey, FileOption::Safe);
|
||||||
|
_languagesKey = 0;
|
||||||
|
writeSettings();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto list = readRecentLanguages();
|
|
||||||
list.erase(
|
|
||||||
ranges::remove_if(
|
|
||||||
list,
|
|
||||||
[&](const Lang::Language &v) { return (v.id == language.id); }),
|
|
||||||
list.end());
|
|
||||||
list.insert(list.begin(), language);
|
|
||||||
|
|
||||||
auto size = sizeof(qint32);
|
auto size = sizeof(qint32);
|
||||||
for (const auto &language : list) {
|
for (const auto &language : list) {
|
||||||
|
@ -4171,7 +4169,33 @@ void pushRecentLanguage(const Lang::Language &language) {
|
||||||
file.writeEncrypted(data, SettingsKey);
|
file.writeEncrypted(data, SettingsKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<Lang::Language> readRecentLanguages() {
|
void pushRecentLanguage(const Lang::Language &language) {
|
||||||
|
if (language.id.startsWith('#')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto list = readRecentLanguages();
|
||||||
|
list.erase(
|
||||||
|
ranges::remove_if(
|
||||||
|
list,
|
||||||
|
[&](const Lang::Language &v) { return (v.id == language.id); }),
|
||||||
|
end(list));
|
||||||
|
list.insert(list.begin(), language);
|
||||||
|
|
||||||
|
saveRecentLanguages(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeRecentLanguage(const QString &id) {
|
||||||
|
auto list = readRecentLanguages();
|
||||||
|
list.erase(
|
||||||
|
ranges::remove_if(
|
||||||
|
list,
|
||||||
|
[&](const Lang::Language &v) { return (v.id == id); }),
|
||||||
|
end(list));
|
||||||
|
|
||||||
|
saveRecentLanguages(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Lang::Language> readRecentLanguages() {
|
||||||
FileReadDescriptor languages;
|
FileReadDescriptor languages;
|
||||||
if (!_languagesKey || !readEncryptedFile(languages, _languagesKey, FileOption::Safe, SettingsKey)) {
|
if (!_languagesKey || !readEncryptedFile(languages, _languagesKey, FileOption::Safe, SettingsKey)) {
|
||||||
return {};
|
return {};
|
||||||
|
@ -4181,7 +4205,7 @@ QVector<Lang::Language> readRecentLanguages() {
|
||||||
if (count <= 0) {
|
if (count <= 0) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto result = QVector<Lang::Language>();
|
auto result = std::vector<Lang::Language>();
|
||||||
result.reserve(count);
|
result.reserve(count);
|
||||||
for (auto i = 0; i != count; ++i) {
|
for (auto i = 0; i != count; ++i) {
|
||||||
auto language = Lang::Language();
|
auto language = Lang::Language();
|
||||||
|
|
|
@ -146,7 +146,9 @@ Window::Theme::Saved readThemeAfterSwitch();
|
||||||
|
|
||||||
void writeLangPack();
|
void writeLangPack();
|
||||||
void pushRecentLanguage(const Lang::Language &language);
|
void pushRecentLanguage(const Lang::Language &language);
|
||||||
QVector<Lang::Language> readRecentLanguages();
|
std::vector<Lang::Language> readRecentLanguages();
|
||||||
|
void saveRecentLanguages(const std::vector<Lang::Language> &list);
|
||||||
|
void removeRecentLanguage(const QString &id);
|
||||||
|
|
||||||
void writeRecentHashtagsAndBots();
|
void writeRecentHashtagsAndBots();
|
||||||
void readRecentHashtagsAndBots();
|
void readRecentHashtagsAndBots();
|
||||||
|
|
|
@ -22,6 +22,7 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
|
constexpr auto kThemeFileSizeLimit = 5 * 1024 * 1024;
|
||||||
constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
constexpr auto kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
||||||
|
constexpr auto kBackgroundSizeLimit = 25 * 1024 * 1024;
|
||||||
constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
|
constexpr auto kThemeSchemeSizeLimit = 1024 * 1024;
|
||||||
constexpr auto kMinimumTiledSize = 512;
|
constexpr auto kMinimumTiledSize = 512;
|
||||||
constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
|
constexpr auto kNightThemeFile = str_const(":/gui/night.tdesktop-theme");
|
||||||
|
@ -291,12 +292,20 @@ bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!backgroundContent.isEmpty()) {
|
if (!backgroundContent.isEmpty()) {
|
||||||
|
auto check = QBuffer(&backgroundContent);
|
||||||
|
auto reader = QImageReader(&check);
|
||||||
|
const auto size = reader.size();
|
||||||
|
if (size.isEmpty()
|
||||||
|
|| (size.width() * size.height() > kBackgroundSizeLimit)) {
|
||||||
|
LOG(("Theme Error: bad background image size in the theme file."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
auto background = App::readImage(backgroundContent);
|
auto background = App::readImage(backgroundContent);
|
||||||
if (background.isNull()) {
|
if (background.isNull()) {
|
||||||
LOG(("Theme Error: could not read background image in the theme file."));
|
LOG(("Theme Error: could not read background image in the theme file."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QBuffer buffer(&cache.background);
|
auto buffer = QBuffer(&cache.background);
|
||||||
if (!background.save(&buffer, "BMP")) {
|
if (!background.save(&buffer, "BMP")) {
|
||||||
LOG(("Theme Error: could not write background image as a BMP to cache."));
|
LOG(("Theme Error: could not write background image as a BMP to cache."));
|
||||||
return false;
|
return false;
|
||||||
|
|
Loading…
Reference in New Issue