711 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			711 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
This file is part of Telegram Desktop,
 | 
						|
the official desktop application for the Telegram messaging service.
 | 
						|
 | 
						|
For license and copyright information please follow this link:
 | 
						|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
						|
*/
 | 
						|
#include "settings/settings_chat.h"
 | 
						|
 | 
						|
#include "settings/settings_common.h"
 | 
						|
#include "boxes/connection_box.h"
 | 
						|
#include "boxes/stickers_box.h"
 | 
						|
#include "boxes/background_box.h"
 | 
						|
#include "boxes/download_path_box.h"
 | 
						|
#include "ui/wrap/vertical_layout.h"
 | 
						|
#include "ui/wrap/slide_wrap.h"
 | 
						|
#include "ui/widgets/checkbox.h"
 | 
						|
#include "ui/widgets/labels.h"
 | 
						|
#include "ui/effects/radial_animation.h"
 | 
						|
#include "lang/lang_keys.h"
 | 
						|
#include "window/themes/window_theme_editor.h"
 | 
						|
#include "window/themes/window_theme.h"
 | 
						|
#include "info/profile/info_profile_button.h"
 | 
						|
#include "storage/localstorage.h"
 | 
						|
#include "core/file_utilities.h"
 | 
						|
#include "mainwidget.h"
 | 
						|
#include "styles/style_settings.h"
 | 
						|
 | 
						|
namespace Settings {
 | 
						|
 | 
						|
class BackgroundRow : public Ui::RpWidget {
 | 
						|
public:
 | 
						|
	BackgroundRow(QWidget *parent);
 | 
						|
 | 
						|
protected:
 | 
						|
	void paintEvent(QPaintEvent *e) override;
 | 
						|
 | 
						|
	int resizeGetHeight(int newWidth) override;
 | 
						|
 | 
						|
private:
 | 
						|
	void updateImage();
 | 
						|
 | 
						|
	float64 radialProgress() const;
 | 
						|
	bool radialLoading() const;
 | 
						|
	QRect radialRect() const;
 | 
						|
	void radialStart();
 | 
						|
	TimeMs radialTimeShift() const;
 | 
						|
	void step_radial(TimeMs ms, bool timer);
 | 
						|
 | 
						|
	QPixmap _background;
 | 
						|
	object_ptr<Ui::LinkButton> _chooseFromGallery;
 | 
						|
	object_ptr<Ui::LinkButton> _chooseFromFile;
 | 
						|
 | 
						|
	Ui::RadialAnimation _radial;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
#ifndef OS_WIN_STORE
 | 
						|
class DownloadPathRow : public Ui::RpWidget {
 | 
						|
public:
 | 
						|
	DownloadPathRow(QWidget *parent);
 | 
						|
 | 
						|
private:
 | 
						|
	void setupControls();
 | 
						|
 | 
						|
};
 | 
						|
#endif // OS_WIN_STORE
 | 
						|
 | 
						|
void ChooseFromFile(not_null<QWidget*> parent);
 | 
						|
 | 
						|
BackgroundRow::BackgroundRow(QWidget *parent) : RpWidget(parent)
 | 
						|
, _chooseFromGallery(
 | 
						|
	this,
 | 
						|
	lang(lng_settings_bg_from_gallery),
 | 
						|
	st::settingsLink)
 | 
						|
, _chooseFromFile(this, lang(lng_settings_bg_from_file), st::settingsLink)
 | 
						|
, _radial(animation(this, &BackgroundRow::step_radial)) {
 | 
						|
	updateImage();
 | 
						|
 | 
						|
	_chooseFromGallery->addClickHandler([] {
 | 
						|
		Ui::show(Box<BackgroundBox>());
 | 
						|
	});
 | 
						|
	_chooseFromFile->addClickHandler([=] {
 | 
						|
		ChooseFromFile(this);
 | 
						|
	});
 | 
						|
 | 
						|
	using Update = const Window::Theme::BackgroundUpdate;
 | 
						|
	base::ObservableViewer(
 | 
						|
		*Window::Theme::Background()
 | 
						|
	) | rpl::filter([](const Update &update) {
 | 
						|
		return (update.type == Update::Type::New
 | 
						|
			|| update.type == Update::Type::Start
 | 
						|
			|| update.type == Update::Type::Changed);
 | 
						|
	}) | rpl::start_with_next([=] {
 | 
						|
		updateImage();
 | 
						|
	}, lifetime());
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundRow::paintEvent(QPaintEvent *e) {
 | 
						|
	Painter p(this);
 | 
						|
 | 
						|
	bool radial = false;
 | 
						|
	float64 radialOpacity = 0;
 | 
						|
	if (_radial.animating()) {
 | 
						|
		_radial.step(getms());
 | 
						|
		radial = _radial.animating();
 | 
						|
		radialOpacity = _radial.opacity();
 | 
						|
	}
 | 
						|
	if (radial) {
 | 
						|
		const auto backThumb = App::main()->newBackgroundThumb();
 | 
						|
		if (backThumb->isNull()) {
 | 
						|
			p.drawPixmap(0, 0, _background);
 | 
						|
		} else {
 | 
						|
			const auto &pix = backThumb->pixBlurred(
 | 
						|
				Data::FileOrigin(),
 | 
						|
				st::settingsBackgroundThumb);
 | 
						|
			const auto factor = cIntRetinaFactor();
 | 
						|
			p.drawPixmap(
 | 
						|
				0,
 | 
						|
				0,
 | 
						|
				st::settingsBackgroundThumb,
 | 
						|
				st::settingsBackgroundThumb,
 | 
						|
				pix,
 | 
						|
				0,
 | 
						|
				(pix.height() - st::settingsBackgroundThumb * factor) / 2,
 | 
						|
				st::settingsBackgroundThumb * factor,
 | 
						|
				st::settingsBackgroundThumb * factor);
 | 
						|
		}
 | 
						|
 | 
						|
		const auto outer = radialRect();
 | 
						|
		const auto inner = QRect(
 | 
						|
			QPoint(
 | 
						|
				outer.x() + (outer.width() - st::radialSize.width()) / 2,
 | 
						|
				outer.y() + (outer.height() - st::radialSize.height()) / 2),
 | 
						|
			st::radialSize);
 | 
						|
		p.setPen(Qt::NoPen);
 | 
						|
		p.setOpacity(radialOpacity);
 | 
						|
		p.setBrush(st::radialBg);
 | 
						|
 | 
						|
		{
 | 
						|
			PainterHighQualityEnabler hq(p);
 | 
						|
			p.drawEllipse(inner);
 | 
						|
		}
 | 
						|
 | 
						|
		p.setOpacity(1);
 | 
						|
		const auto arc = inner.marginsRemoved(QMargins(
 | 
						|
			st::radialLine,
 | 
						|
			st::radialLine,
 | 
						|
			st::radialLine,
 | 
						|
			st::radialLine));
 | 
						|
		_radial.draw(p, arc, st::radialLine, st::radialFg);
 | 
						|
	} else {
 | 
						|
		p.drawPixmap(0, 0, _background);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int BackgroundRow::resizeGetHeight(int newWidth) {
 | 
						|
	auto linkTop = st::settingsFromGalleryTop;
 | 
						|
	auto linkLeft = st::settingsBackgroundThumb + st::settingsThumbSkip;
 | 
						|
	auto linkWidth = newWidth - linkLeft;
 | 
						|
	_chooseFromGallery->resizeToWidth(
 | 
						|
		qMin(linkWidth, _chooseFromGallery->naturalWidth()));
 | 
						|
	_chooseFromFile->resizeToWidth(
 | 
						|
		qMin(linkWidth, _chooseFromFile->naturalWidth()));
 | 
						|
	_chooseFromGallery->moveToLeft(linkLeft, linkTop, newWidth);
 | 
						|
	linkTop += _chooseFromGallery->height() + st::settingsFromFileTop;
 | 
						|
	_chooseFromFile->moveToLeft(linkLeft, linkTop, newWidth);
 | 
						|
	return st::settingsBackgroundThumb;
 | 
						|
}
 | 
						|
 | 
						|
float64 BackgroundRow::radialProgress() const {
 | 
						|
	return App::main()->chatBackgroundProgress();
 | 
						|
}
 | 
						|
 | 
						|
bool BackgroundRow::radialLoading() const {
 | 
						|
	const auto main = App::main();
 | 
						|
	if (main->chatBackgroundLoading()) {
 | 
						|
		main->checkChatBackground();
 | 
						|
		if (main->chatBackgroundLoading()) {
 | 
						|
			return true;
 | 
						|
		} else {
 | 
						|
			const_cast<BackgroundRow*>(this)->updateImage();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
QRect BackgroundRow::radialRect() const {
 | 
						|
	return QRect(
 | 
						|
		0,
 | 
						|
		0,
 | 
						|
		st::settingsBackgroundThumb,
 | 
						|
		st::settingsBackgroundThumb);
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundRow::radialStart() {
 | 
						|
	if (radialLoading() && !_radial.animating()) {
 | 
						|
		_radial.start(radialProgress());
 | 
						|
		if (const auto shift = radialTimeShift()) {
 | 
						|
			_radial.update(
 | 
						|
				radialProgress(),
 | 
						|
				!radialLoading(),
 | 
						|
				getms() + shift);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
TimeMs BackgroundRow::radialTimeShift() const {
 | 
						|
	return st::radialDuration;
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundRow::step_radial(TimeMs ms, bool timer) {
 | 
						|
	_radial.update(
 | 
						|
		radialProgress(),
 | 
						|
		!radialLoading(),
 | 
						|
		ms + radialTimeShift());
 | 
						|
	if (timer && _radial.animating()) {
 | 
						|
		rtlupdate(radialRect());
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void BackgroundRow::updateImage() {
 | 
						|
	int32 size = st::settingsBackgroundThumb * cIntRetinaFactor();
 | 
						|
	QImage back(size, size, QImage::Format_ARGB32_Premultiplied);
 | 
						|
	back.setDevicePixelRatio(cRetinaFactor());
 | 
						|
	{
 | 
						|
		Painter p(&back);
 | 
						|
		PainterHighQualityEnabler hq(p);
 | 
						|
 | 
						|
		const auto &pix = Window::Theme::Background()->pixmap();
 | 
						|
		const auto sx = (pix.width() > pix.height())
 | 
						|
			? ((pix.width() - pix.height()) / 2)
 | 
						|
			: 0;
 | 
						|
		const auto sy = (pix.height() > pix.width())
 | 
						|
			? ((pix.height() - pix.width()) / 2)
 | 
						|
			: 0;
 | 
						|
		const auto s = (pix.width() > pix.height())
 | 
						|
			? pix.height()
 | 
						|
			: pix.width();
 | 
						|
		p.drawPixmap(
 | 
						|
			0,
 | 
						|
			0,
 | 
						|
			st::settingsBackgroundThumb,
 | 
						|
			st::settingsBackgroundThumb,
 | 
						|
			pix,
 | 
						|
			sx,
 | 
						|
			sy,
 | 
						|
			s,
 | 
						|
			s);
 | 
						|
	}
 | 
						|
	Images::prepareRound(back, ImageRoundRadius::Small);
 | 
						|
	_background = App::pixmapFromImageInPlace(std::move(back));
 | 
						|
	_background.setDevicePixelRatio(cRetinaFactor());
 | 
						|
 | 
						|
	rtlupdate(radialRect());
 | 
						|
 | 
						|
	if (radialLoading()) {
 | 
						|
		radialStart();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void ChooseFromFile(not_null<QWidget*> parent) {
 | 
						|
	const auto imgExtensions = cImgExtensions();
 | 
						|
	auto filters = QStringList(
 | 
						|
		qsl("Theme files (*.tdesktop-theme *.tdesktop-palette *")
 | 
						|
		+ imgExtensions.join(qsl(" *"))
 | 
						|
		+ qsl(")"));
 | 
						|
	filters.push_back(FileDialog::AllFilesFilter());
 | 
						|
	const auto callback = [=](const FileDialog::OpenResult &result) {
 | 
						|
		if (result.paths.isEmpty() && result.remoteContent.isEmpty()) {
 | 
						|
			return;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!result.paths.isEmpty()) {
 | 
						|
			const auto filePath = result.paths.front();
 | 
						|
			const auto hasExtension = [&](QLatin1String extension) {
 | 
						|
				return filePath.endsWith(extension, Qt::CaseInsensitive);
 | 
						|
			};
 | 
						|
			if (hasExtension(qstr(".tdesktop-theme"))
 | 
						|
				|| hasExtension(qstr(".tdesktop-palette"))) {
 | 
						|
				Window::Theme::Apply(filePath);
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		auto image = result.remoteContent.isEmpty()
 | 
						|
			? App::readImage(result.paths.front())
 | 
						|
			: App::readImage(result.remoteContent);
 | 
						|
		if (image.isNull() || image.width() <= 0 || image.height() <= 0) {
 | 
						|
			return;
 | 
						|
		} else if (image.width() > 4096 * image.height()) {
 | 
						|
			image = image.copy(
 | 
						|
				(image.width() - 4096 * image.height()) / 2,
 | 
						|
				0,
 | 
						|
				4096 * image.height(),
 | 
						|
				image.height());
 | 
						|
		} else if (image.height() > 4096 * image.width()) {
 | 
						|
			image = image.copy(
 | 
						|
				0,
 | 
						|
				(image.height() - 4096 * image.width()) / 2,
 | 
						|
				image.width(),
 | 
						|
				4096 * image.width());
 | 
						|
		}
 | 
						|
 | 
						|
		Window::Theme::Background()->setImage(
 | 
						|
			Window::Theme::kCustomBackground,
 | 
						|
			std::move(image));
 | 
						|
		Window::Theme::Background()->setTile(false);
 | 
						|
	};
 | 
						|
	FileDialog::GetOpenPath(
 | 
						|
		parent.get(),
 | 
						|
		lang(lng_choose_image),
 | 
						|
		filters.join(qsl(";;")),
 | 
						|
		crl::guard(parent, callback));
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
#ifndef OS_WIN_STORE
 | 
						|
 | 
						|
QString DownloadPathText() {
 | 
						|
	if (Global::DownloadPath().isEmpty()) {
 | 
						|
		return lang(lng_download_path_default);
 | 
						|
	} else if (Global::DownloadPath() == qsl("tmp")) {
 | 
						|
		return lang(lng_download_path_temp);
 | 
						|
	}
 | 
						|
	return QDir::toNativeSeparators(Global::DownloadPath());
 | 
						|
}
 | 
						|
 | 
						|
DownloadPathRow::DownloadPathRow(QWidget *parent) : RpWidget(parent) {
 | 
						|
	setupControls();
 | 
						|
}
 | 
						|
 | 
						|
void DownloadPathRow::setupControls() {
 | 
						|
	const auto label = Ui::CreateChild<Ui::FlatLabel>(
 | 
						|
		this,
 | 
						|
		Lang::Viewer(lng_download_path_label),
 | 
						|
		st::settingsLinkLabel);
 | 
						|
	const auto link = Ui::CreateChild<Ui::LinkButton>(
 | 
						|
		this,
 | 
						|
		DownloadPathText(),
 | 
						|
		st::settingsLink);
 | 
						|
 | 
						|
	base::ObservableViewer(
 | 
						|
		Global::RefDownloadPathChanged()
 | 
						|
	) | rpl::map([] {
 | 
						|
		return DownloadPathText();
 | 
						|
	}) | rpl::start_with_next([=](const QString &text) {
 | 
						|
		link->setText(text);
 | 
						|
	}, link->lifetime());
 | 
						|
 | 
						|
	link->addClickHandler([] {
 | 
						|
		Ui::show(Box<DownloadPathBox>());
 | 
						|
	});
 | 
						|
 | 
						|
	rpl::combine(
 | 
						|
		widthValue(),
 | 
						|
		label->sizeValue(),
 | 
						|
		link->widthValue()
 | 
						|
	) | rpl::start_with_next([=](int width, QSize labelSize, int possible) {
 | 
						|
		const auto space = st::settingsLinkLabel.style.font->spacew;
 | 
						|
		link->resizeToNaturalWidth(width - labelSize.width() - space);
 | 
						|
		if (link->width() == possible) {
 | 
						|
			label->moveToLeft(0, 0);
 | 
						|
			link->moveToLeft(labelSize.width() + space, 0);
 | 
						|
			resize(width, labelSize.height());
 | 
						|
		}
 | 
						|
	}, link->lifetime());
 | 
						|
}
 | 
						|
 | 
						|
#endif // OS_WIN_STORE
 | 
						|
 | 
						|
void SetupChatOptions(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	AddDivider(container);
 | 
						|
	AddSkip(container, st::settingsCheckboxesSkip);
 | 
						|
 | 
						|
	auto wrap = object_ptr<Ui::VerticalLayout>(container);
 | 
						|
	const auto inner = wrap.data();
 | 
						|
	container->add(object_ptr<Ui::OverrideMargins>(
 | 
						|
		container,
 | 
						|
		std::move(wrap),
 | 
						|
		QMargins(0, 0, 0, st::settingsCheckbox.margin.bottom())));
 | 
						|
 | 
						|
	const auto checkbox = [&](LangKey label, bool checked) {
 | 
						|
		return object_ptr<Ui::Checkbox>(
 | 
						|
			container,
 | 
						|
			lang(label),
 | 
						|
			checked,
 | 
						|
			st::settingsCheckbox);
 | 
						|
	};
 | 
						|
	const auto add = [&](LangKey label, bool checked, auto &&handle) {
 | 
						|
		base::ObservableViewer(
 | 
						|
			inner->add(
 | 
						|
				checkbox(label, checked),
 | 
						|
				st::settingsCheckboxPadding
 | 
						|
			)->checkedChanged
 | 
						|
		) | rpl::start_with_next(
 | 
						|
			std::move(handle),
 | 
						|
			inner->lifetime());
 | 
						|
	};
 | 
						|
	add(
 | 
						|
		lng_settings_replace_emojis,
 | 
						|
		Global::ReplaceEmoji(),
 | 
						|
		[](bool checked) {
 | 
						|
			Global::SetReplaceEmoji(checked);
 | 
						|
			Global::RefReplaceEmojiChanged().notify();
 | 
						|
			Local::writeUserSettings();
 | 
						|
		});
 | 
						|
 | 
						|
	add(
 | 
						|
		lng_settings_suggest_emoji,
 | 
						|
		Global::SuggestEmoji(),
 | 
						|
		[](bool checked) {
 | 
						|
			Global::SetSuggestEmoji(checked);
 | 
						|
			Local::writeUserSettings();
 | 
						|
		});
 | 
						|
 | 
						|
	add(
 | 
						|
		lng_settings_suggest_by_emoji,
 | 
						|
		Global::SuggestStickersByEmoji(),
 | 
						|
		[](bool checked) {
 | 
						|
			Global::SetSuggestStickersByEmoji(checked);
 | 
						|
			Local::writeUserSettings();
 | 
						|
		});
 | 
						|
 | 
						|
	const auto dontask = inner->add(
 | 
						|
		checkbox(
 | 
						|
			lng_download_path_dont_ask,
 | 
						|
			!Global::AskDownloadPath()),
 | 
						|
#ifndef OS_WIN_STORE
 | 
						|
		st::settingsAskPathPadding);
 | 
						|
#else // OS_WIN_STORE
 | 
						|
		st::settingsCheckboxPadding);
 | 
						|
#endif // OS_WIN_STORE
 | 
						|
 | 
						|
#ifndef OS_WIN_STORE
 | 
						|
	const auto showpath = Ui::AttachAsChild(
 | 
						|
		dontask,
 | 
						|
		rpl::event_stream<bool>());
 | 
						|
	const auto padding = st::settingsDownloadPathPadding;
 | 
						|
	const auto path = container->add(
 | 
						|
		object_ptr<Ui::SlideWrap<DownloadPathRow>>(
 | 
						|
			container,
 | 
						|
			object_ptr<DownloadPathRow>(container),
 | 
						|
			QMargins(
 | 
						|
				(padding.left()
 | 
						|
					+ st::settingsCheckbox.checkPosition.x()
 | 
						|
					+ st::defaultCheck.diameter
 | 
						|
					+ st::settingsCheckbox.textPosition.x()
 | 
						|
					- st::settingsCheckbox.margin.left()),
 | 
						|
				padding.top(),
 | 
						|
				padding.right(),
 | 
						|
				padding.bottom())));
 | 
						|
	AddSkip(container, st::settingsCheckboxPadding.bottom());
 | 
						|
	path->toggleOn(
 | 
						|
		showpath->events_starting_with(!Global::AskDownloadPath()));
 | 
						|
#endif // OS_WIN_STORE
 | 
						|
 | 
						|
	base::ObservableViewer(
 | 
						|
		dontask->checkedChanged
 | 
						|
	) | rpl::start_with_next([=](bool checked) {
 | 
						|
		Global::SetAskDownloadPath(!checked);
 | 
						|
		Local::writeUserSettings();
 | 
						|
 | 
						|
#ifndef OS_WIN_STORE
 | 
						|
		showpath->fire_copy(checked);
 | 
						|
#endif // OS_WIN_STORE
 | 
						|
 | 
						|
	}, inner->lifetime());
 | 
						|
 | 
						|
	AddSkip(container, st::settingsCheckboxesSkip);
 | 
						|
}
 | 
						|
 | 
						|
void SetupSendKey(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	AddDivider(container);
 | 
						|
	const auto skip = st::settingsSendTypeSkip;
 | 
						|
	const auto full = st::settingsCheckboxesSkip + skip;
 | 
						|
	AddSkip(container, full);
 | 
						|
 | 
						|
	enum class SendByType {
 | 
						|
		Enter,
 | 
						|
		CtrlEnter,
 | 
						|
	};
 | 
						|
 | 
						|
	const auto group = std::make_shared<Ui::RadioenumGroup<SendByType>>(
 | 
						|
		cCtrlEnter() ? SendByType::CtrlEnter : SendByType::Enter);
 | 
						|
	auto wrap = object_ptr<Ui::VerticalLayout>(container);
 | 
						|
	const auto inner = wrap.data();
 | 
						|
	container->add(
 | 
						|
		object_ptr<Ui::OverrideMargins>(
 | 
						|
			container,
 | 
						|
			std::move(wrap),
 | 
						|
			QMargins(0, skip, 0, skip)));
 | 
						|
 | 
						|
	AddSkip(container, full);
 | 
						|
 | 
						|
	const auto add = [&](
 | 
						|
			SendByType value,
 | 
						|
			LangKey key,
 | 
						|
			style::margins padding) {
 | 
						|
		inner->add(
 | 
						|
			object_ptr<Ui::Radioenum<SendByType>>(
 | 
						|
				inner,
 | 
						|
				group,
 | 
						|
				value,
 | 
						|
				lang(key),
 | 
						|
				st::settingsSendType),
 | 
						|
			st::settingsSendTypePadding);
 | 
						|
	};
 | 
						|
	const auto small = st::settingsSendTypePadding;
 | 
						|
	const auto top = skip;
 | 
						|
	add(
 | 
						|
		SendByType::Enter,
 | 
						|
		lng_settings_send_enter,
 | 
						|
		{ small.left(), small.top() + top, small.right(), small.bottom() });
 | 
						|
	add(
 | 
						|
		SendByType::CtrlEnter,
 | 
						|
		((cPlatform() == dbipMac || cPlatform() == dbipMacOld)
 | 
						|
			? lng_settings_send_cmdenter
 | 
						|
			: lng_settings_send_ctrlenter),
 | 
						|
		{ small.left(), small.top(), small.right(), small.bottom() + top });
 | 
						|
	group->setChangedCallback([](SendByType value) {
 | 
						|
		cSetCtrlEnter(value == SendByType::CtrlEnter);
 | 
						|
		if (App::main()) {
 | 
						|
			App::main()->ctrlEnterSubmitUpdated();
 | 
						|
		}
 | 
						|
		Local::writeUserSettings();
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void SetupMediaOptions(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	AddDivider(container);
 | 
						|
	AddSkip(container);
 | 
						|
 | 
						|
	AddButton(
 | 
						|
		container,
 | 
						|
		lng_media_auto_settings,
 | 
						|
		st::settingsButton
 | 
						|
	)->addClickHandler([] {
 | 
						|
		Ui::show(Box<AutoDownloadBox>());
 | 
						|
	});
 | 
						|
 | 
						|
	AddButton(
 | 
						|
		container,
 | 
						|
		lng_stickers_you_have,
 | 
						|
		st::settingsButton
 | 
						|
	)->addClickHandler([] {
 | 
						|
		Ui::show(Box<StickersBox>(StickersBox::Section::Installed));
 | 
						|
	});
 | 
						|
 | 
						|
	AddSkip(container);
 | 
						|
}
 | 
						|
 | 
						|
void SetupChatBackground(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	AddDivider(container);
 | 
						|
	AddSkip(container, st::settingsCheckboxesSkip);
 | 
						|
 | 
						|
	AddSubsectionTitle(container, lng_settings_section_background);
 | 
						|
 | 
						|
	container->add(
 | 
						|
		object_ptr<BackgroundRow>(container),
 | 
						|
		st::settingsBackgroundPadding);
 | 
						|
 | 
						|
	const auto skipTop = st::settingsCheckbox.margin.top();
 | 
						|
	const auto skipBottom = st::settingsCheckbox.margin.bottom();
 | 
						|
	auto wrap = object_ptr<Ui::VerticalLayout>(container);
 | 
						|
	const auto inner = wrap.data();
 | 
						|
	container->add(
 | 
						|
		object_ptr<Ui::OverrideMargins>(
 | 
						|
			container,
 | 
						|
			std::move(wrap),
 | 
						|
			QMargins(0, skipTop, 0, skipBottom)));
 | 
						|
 | 
						|
	AddSkip(container, st::settingsTileSkip);
 | 
						|
 | 
						|
	const auto tile = inner->add(
 | 
						|
		object_ptr<Ui::Checkbox>(
 | 
						|
			inner,
 | 
						|
			lang(lng_settings_bg_tile),
 | 
						|
			Window::Theme::Background()->tile(),
 | 
						|
			st::settingsCheckbox),
 | 
						|
		st::settingsSendTypePadding);
 | 
						|
	const auto adaptive = inner->add(
 | 
						|
		object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
 | 
						|
			inner,
 | 
						|
			object_ptr<Ui::Checkbox>(
 | 
						|
				inner,
 | 
						|
				lang(lng_settings_adaptive_wide),
 | 
						|
				Global::AdaptiveForWide(),
 | 
						|
				st::settingsCheckbox),
 | 
						|
			st::settingsSendTypePadding));
 | 
						|
 | 
						|
	base::ObservableViewer(
 | 
						|
		tile->checkedChanged
 | 
						|
	) | rpl::start_with_next([](bool checked) {
 | 
						|
		Window::Theme::Background()->setTile(checked);
 | 
						|
	}, tile->lifetime());
 | 
						|
 | 
						|
	using Update = const Window::Theme::BackgroundUpdate;
 | 
						|
	base::ObservableViewer(
 | 
						|
		*Window::Theme::Background()
 | 
						|
	) | rpl::filter([](const Update &update) {
 | 
						|
		return (update.type == Update::Type::Changed);
 | 
						|
	}) | rpl::map([] {
 | 
						|
		return Window::Theme::Background()->tile();
 | 
						|
	}) | rpl::start_with_next([=](bool tiled) {
 | 
						|
		tile->setChecked(tiled);
 | 
						|
	}, tile->lifetime());
 | 
						|
 | 
						|
	adaptive->toggleOn(rpl::single(
 | 
						|
		rpl::empty_value()
 | 
						|
	) | rpl::then(base::ObservableViewer(
 | 
						|
		Adaptive::Changed()
 | 
						|
	)) | rpl::map([] {
 | 
						|
		return (Global::AdaptiveChatLayout() == Adaptive::ChatLayout::Wide);
 | 
						|
	}));
 | 
						|
 | 
						|
	base::ObservableViewer(
 | 
						|
		adaptive->entity()->checkedChanged
 | 
						|
	) | rpl::start_with_next([](bool checked) {
 | 
						|
		Global::SetAdaptiveForWide(checked);
 | 
						|
		Adaptive::Changed().notify();
 | 
						|
		Local::writeUserSettings();
 | 
						|
	}, adaptive->lifetime());
 | 
						|
}
 | 
						|
 | 
						|
void SetupNightMode(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	const auto calling = Ui::AttachAsChild(container, 0);
 | 
						|
	AddButton(
 | 
						|
		container,
 | 
						|
		lng_settings_use_night_mode,
 | 
						|
		st::settingsButton
 | 
						|
	)->toggleOn(
 | 
						|
		rpl::single(Window::Theme::IsNightMode())
 | 
						|
	)->toggledValue(
 | 
						|
	) | rpl::start_with_next([=](bool toggled) {
 | 
						|
		++*calling;
 | 
						|
		const auto change = [=] {
 | 
						|
			if (!--*calling && toggled != Window::Theme::IsNightMode()) {
 | 
						|
				Window::Theme::ToggleNightMode();
 | 
						|
			}
 | 
						|
		};
 | 
						|
		App::CallDelayed(
 | 
						|
			st::settingsButton.toggle.duration,
 | 
						|
			container,
 | 
						|
			change);
 | 
						|
	}, container->lifetime());
 | 
						|
}
 | 
						|
 | 
						|
void SetupUseDefaultTheme(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	using Update = const Window::Theme::BackgroundUpdate;
 | 
						|
	container->add(
 | 
						|
		object_ptr<Ui::SlideWrap<Button>>(
 | 
						|
			container,
 | 
						|
			object_ptr<Button>(
 | 
						|
				container,
 | 
						|
				Lang::Viewer(lng_settings_bg_use_default),
 | 
						|
				st::settingsButton))
 | 
						|
	)->toggleOn(rpl::single(
 | 
						|
		Window::Theme::SuggestThemeReset()
 | 
						|
	) | rpl::then(base::ObservableViewer(
 | 
						|
		*Window::Theme::Background()
 | 
						|
	) | rpl::filter([](const Update &update) {
 | 
						|
		return (update.type == Update::Type::ApplyingTheme
 | 
						|
			|| update.type == Update::Type::New);
 | 
						|
	}) | rpl::map([] {
 | 
						|
		return Window::Theme::SuggestThemeReset();
 | 
						|
	})))->entity()->addClickHandler([] {
 | 
						|
		Window::Theme::ApplyDefault();
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void SetupThemeOptions(not_null<Ui::VerticalLayout*> container) {
 | 
						|
	AddDivider(container);
 | 
						|
	AddSkip(container);
 | 
						|
 | 
						|
	SetupNightMode(container);
 | 
						|
 | 
						|
	AddButton(
 | 
						|
		container,
 | 
						|
		lng_settings_bg_edit_theme,
 | 
						|
		st::settingsButton
 | 
						|
	)->addClickHandler(App::LambdaDelayed(
 | 
						|
		st::settingsButton.ripple.hideDuration,
 | 
						|
		container,
 | 
						|
		[] { Window::Theme::Editor::Start(); }));
 | 
						|
 | 
						|
	SetupUseDefaultTheme(container);
 | 
						|
 | 
						|
	AddSkip(container);
 | 
						|
}
 | 
						|
 | 
						|
Chat::Chat(QWidget *parent, not_null<UserData*> self)
 | 
						|
: Section(parent)
 | 
						|
, _self(self) {
 | 
						|
	setupContent();
 | 
						|
}
 | 
						|
 | 
						|
void Chat::setupContent() {
 | 
						|
	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
 | 
						|
 | 
						|
	AddSkip(content, st::settingsFirstDividerSkip);
 | 
						|
	SetupChatOptions(content);
 | 
						|
	SetupSendKey(content);
 | 
						|
	SetupMediaOptions(content);
 | 
						|
	SetupChatBackground(content);
 | 
						|
	SetupThemeOptions(content);
 | 
						|
 | 
						|
	Ui::ResizeFitChild(this, content);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Settings
 |