1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "settings/settings_advanced.h"
9 
10 #include "settings/settings_common.h"
11 #include "settings/settings_chat.h"
12 #include "ui/wrap/vertical_layout.h"
13 #include "ui/wrap/slide_wrap.h"
14 #include "ui/widgets/labels.h"
15 #include "ui/widgets/checkbox.h"
16 #include "ui/widgets/buttons.h"
17 #include "ui/gl/gl_detection.h"
18 #include "ui/text/text_utilities.h" // Ui::Text::ToUpper
19 #include "ui/text/format_values.h"
20 #include "ui/boxes/single_choice_box.h"
21 #include "boxes/connection_box.h"
22 #include "boxes/about_box.h"
23 #include "ui/boxes/confirm_box.h"
24 #include "platform/platform_specific.h"
25 #include "ui/platform/ui_platform_window.h"
26 #include "base/platform/base_platform_info.h"
27 #include "window/window_controller.h"
28 #include "window/window_session_controller.h"
29 #include "lang/lang_keys.h"
30 #include "core/update_checker.h"
31 #include "core/application.h"
32 #include "storage/localstorage.h"
33 #include "storage/storage_domain.h"
34 #include "data/data_session.h"
35 #include "main/main_account.h"
36 #include "main/main_domain.h"
37 #include "main/main_session.h"
38 #include "mtproto/facade.h"
39 #include "app.h"
40 #include "styles/style_settings.h"
41 
42 #ifndef TDESKTOP_DISABLE_SPELLCHECK
43 #include "boxes/dictionaries_manager.h"
44 #include "chat_helpers/spellchecker_common.h"
45 #include "spellcheck/platform/platform_spellcheck.h"
46 #endif // !TDESKTOP_DISABLE_SPELLCHECK
47 
48 namespace Settings {
49 
SetupConnectionType(not_null<Window::Controller * > controller,not_null<Main::Account * > account,not_null<Ui::VerticalLayout * > container)50 void SetupConnectionType(
51 		not_null<Window::Controller*> controller,
52 		not_null<Main::Account*> account,
53 		not_null<Ui::VerticalLayout*> container) {
54 	const auto connectionType = [=] {
55 		const auto transport = account->mtp().dctransport();
56 		if (!Core::App().settings().proxy().isEnabled()) {
57 			return transport.isEmpty()
58 				? tr::lng_connection_auto_connecting(tr::now)
59 				: tr::lng_connection_auto(tr::now, lt_transport, transport);
60 		} else {
61 			return transport.isEmpty()
62 				? tr::lng_connection_proxy_connecting(tr::now)
63 				: tr::lng_connection_proxy(tr::now, lt_transport, transport);
64 		}
65 	};
66 	const auto button = AddButtonWithLabel(
67 		container,
68 		tr::lng_settings_connection_type(),
69 		rpl::merge(
70 			Core::App().settings().proxy().connectionTypeChanges(),
71 			// Handle language switch.
72 			tr::lng_connection_auto_connecting() | rpl::to_empty
73 		) | rpl::map(connectionType),
74 		st::settingsButton);
75 	button->addClickHandler([=] {
76 		controller->show(ProxiesBoxController::CreateOwningBox(account));
77 	});
78 }
79 
HasUpdate()80 bool HasUpdate() {
81 	return !Core::UpdaterDisabled();
82 }
83 
SetupUpdate(not_null<Ui::VerticalLayout * > container)84 void SetupUpdate(not_null<Ui::VerticalLayout*> container) {
85 	if (!HasUpdate()) {
86 		return;
87 	}
88 
89 	const auto texts = Ui::CreateChild<rpl::event_stream<QString>>(
90 		container.get());
91 	const auto downloading = Ui::CreateChild<rpl::event_stream<bool>>(
92 		container.get());
93 	const auto version = tr::lng_settings_current_version(
94 		tr::now,
95 		lt_version,
96 		currentVersionText());
97 	const auto toggle = AddButton(
98 		container,
99 		tr::lng_settings_update_automatically(),
100 		st::settingsUpdateToggle);
101 	const auto label = Ui::CreateChild<Ui::FlatLabel>(
102 		toggle.get(),
103 		texts->events(),
104 		st::settingsUpdateState);
105 
106 	const auto options = container->add(
107 		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
108 			container,
109 			object_ptr<Ui::VerticalLayout>(container)));
110 	const auto inner = options->entity();
111 	const auto install = cAlphaVersion() ? nullptr : AddButton(
112 		inner,
113 		tr::lng_settings_install_beta(),
114 		st::settingsButton).get();
115 
116 	const auto check = AddButton(
117 		inner,
118 		tr::lng_settings_check_now(),
119 		st::settingsButton);
120 	const auto update = Ui::CreateChild<Button>(
121 		check.get(),
122 		tr::lng_update_telegram() | Ui::Text::ToUpper(),
123 		st::settingsUpdate);
124 	update->hide();
125 	check->widthValue() | rpl::start_with_next([=](int width) {
126 		update->resizeToWidth(width);
127 		update->moveToLeft(0, 0);
128 	}, update->lifetime());
129 
130 	rpl::combine(
131 		toggle->widthValue(),
132 		label->widthValue()
133 	) | rpl::start_with_next([=] {
134 		label->moveToLeft(
135 			st::settingsUpdateStatePosition.x(),
136 			st::settingsUpdateStatePosition.y());
137 	}, label->lifetime());
138 	label->setAttribute(Qt::WA_TransparentForMouseEvents);
139 
140 	const auto showDownloadProgress = [=](int64 ready, int64 total) {
141 		texts->fire(tr::lng_settings_downloading_update(
142 			tr::now,
143 			lt_progress,
144 			Ui::FormatDownloadText(ready, total)));
145 		downloading->fire(true);
146 	};
147 	const auto setDefaultStatus = [=](const Core::UpdateChecker &checker) {
148 		using State = Core::UpdateChecker::State;
149 		const auto state = checker.state();
150 		switch (state) {
151 		case State::Download:
152 			showDownloadProgress(checker.already(), checker.size());
153 			break;
154 		case State::Ready:
155 			texts->fire(tr::lng_settings_update_ready(tr::now));
156 			update->show();
157 			break;
158 		default:
159 			texts->fire_copy(version);
160 			break;
161 		}
162 	};
163 
164 	toggle->toggleOn(rpl::single(cAutoUpdate()));
165 	toggle->toggledValue(
166 	) | rpl::filter([](bool toggled) {
167 		return (toggled != cAutoUpdate());
168 	}) | rpl::start_with_next([=](bool toggled) {
169 		cSetAutoUpdate(toggled);
170 
171 		Local::writeSettings();
172 		Core::UpdateChecker checker;
173 		if (cAutoUpdate()) {
174 			checker.start();
175 		} else {
176 			checker.stop();
177 			setDefaultStatus(checker);
178 		}
179 	}, toggle->lifetime());
180 
181 	if (install) {
182 		install->toggleOn(rpl::single(cInstallBetaVersion()));
183 		install->toggledValue(
184 		) | rpl::filter([](bool toggled) {
185 			return (toggled != cInstallBetaVersion());
186 		}) | rpl::start_with_next([=](bool toggled) {
187 			cSetInstallBetaVersion(toggled);
188 			Core::App().writeInstallBetaVersionsSetting();
189 
190 			Core::UpdateChecker checker;
191 			checker.stop();
192 			if (toggled) {
193 				cSetLastUpdateCheck(0);
194 			}
195 			checker.start();
196 		}, toggle->lifetime());
197 	}
198 
199 	Core::UpdateChecker checker;
200 	options->toggleOn(rpl::combine(
201 		toggle->toggledValue(),
202 		downloading->events_starting_with(
203 			checker.state() == Core::UpdateChecker::State::Download)
204 	) | rpl::map([](bool check, bool downloading) {
205 		return check && !downloading;
206 	}));
207 
208 	checker.checking() | rpl::start_with_next([=] {
209 		options->setAttribute(Qt::WA_TransparentForMouseEvents);
210 		texts->fire(tr::lng_settings_update_checking(tr::now));
211 		downloading->fire(false);
212 	}, options->lifetime());
213 	checker.isLatest() | rpl::start_with_next([=] {
214 		options->setAttribute(Qt::WA_TransparentForMouseEvents, false);
215 		texts->fire(tr::lng_settings_latest_installed(tr::now));
216 		downloading->fire(false);
217 	}, options->lifetime());
218 	checker.progress(
219 	) | rpl::start_with_next([=](Core::UpdateChecker::Progress progress) {
220 		showDownloadProgress(progress.already, progress.size);
221 	}, options->lifetime());
222 	checker.failed() | rpl::start_with_next([=] {
223 		options->setAttribute(Qt::WA_TransparentForMouseEvents, false);
224 		texts->fire(tr::lng_settings_update_fail(tr::now));
225 		downloading->fire(false);
226 	}, options->lifetime());
227 	checker.ready() | rpl::start_with_next([=] {
228 		options->setAttribute(Qt::WA_TransparentForMouseEvents, false);
229 		texts->fire(tr::lng_settings_update_ready(tr::now));
230 		update->show();
231 		downloading->fire(false);
232 	}, options->lifetime());
233 
234 	setDefaultStatus(checker);
235 
236 	check->addClickHandler([] {
237 		Core::UpdateChecker checker;
238 
239 		cSetLastUpdateCheck(0);
240 		checker.start();
241 	});
242 	update->addClickHandler([] {
243 		if (!Core::UpdaterDisabled()) {
244 			Core::checkReadyUpdate();
245 		}
246 		App::restart();
247 	});
248 }
249 
HasSystemSpellchecker()250 bool HasSystemSpellchecker() {
251 #ifdef TDESKTOP_DISABLE_SPELLCHECK
252 	return false;
253 #endif // TDESKTOP_DISABLE_SPELLCHECK
254 	return true;
255 }
256 
SetupSpellchecker(not_null<Window::SessionController * > controller,not_null<Ui::VerticalLayout * > container)257 void SetupSpellchecker(
258 		not_null<Window::SessionController*> controller,
259 		not_null<Ui::VerticalLayout*> container) {
260 #ifndef TDESKTOP_DISABLE_SPELLCHECK
261 	const auto session = &controller->session();
262 	const auto settings = &Core::App().settings();
263 	const auto isSystem = Platform::Spellchecker::IsSystemSpellchecker();
264 	const auto button = AddButton(
265 		container,
266 		isSystem
267 			? tr::lng_settings_system_spellchecker()
268 			: tr::lng_settings_custom_spellchecker(),
269 		st::settingsButton
270 	)->toggleOn(
271 		rpl::single(settings->spellcheckerEnabled())
272 	);
273 
274 	button->toggledValue(
275 	) | rpl::filter([=](bool enabled) {
276 		return (enabled != settings->spellcheckerEnabled());
277 	}) | rpl::start_with_next([=](bool enabled) {
278 		settings->setSpellcheckerEnabled(enabled);
279 		Core::App().saveSettingsDelayed();
280 	}, container->lifetime());
281 
282 	if (isSystem) {
283 		return;
284 	}
285 
286 	const auto sliding = container->add(
287 		object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
288 			container,
289 			object_ptr<Ui::VerticalLayout>(container)));
290 
291 	AddButton(
292 		sliding->entity(),
293 		tr::lng_settings_auto_download_dictionaries(),
294 		st::settingsButton
295 	)->toggleOn(
296 		rpl::single(settings->autoDownloadDictionaries())
297 	)->toggledValue(
298 	) | rpl::filter([=](bool enabled) {
299 		return (enabled != settings->autoDownloadDictionaries());
300 	}) | rpl::start_with_next([=](bool enabled) {
301 		settings->setAutoDownloadDictionaries(enabled);
302 		Core::App().saveSettingsDelayed();
303 	}, sliding->entity()->lifetime());
304 
305 	AddButtonWithLabel(
306 		sliding->entity(),
307 		tr::lng_settings_manage_dictionaries(),
308 		Spellchecker::ButtonManageDictsState(session),
309 		st::settingsButton
310 	)->addClickHandler([=] {
311 		controller->show(Box<Ui::ManageDictionariesBox>(controller));
312 	});
313 
314 	button->toggledValue(
315 	) | rpl::start_with_next([=](bool enabled) {
316 		sliding->toggle(enabled, anim::type::normal);
317 	}, container->lifetime());
318 #endif // !TDESKTOP_DISABLE_SPELLCHECK
319 }
320 
SetupSystemIntegrationContent(Window::SessionController * controller,not_null<Ui::VerticalLayout * > container)321 void SetupSystemIntegrationContent(
322 		Window::SessionController *controller,
323 		not_null<Ui::VerticalLayout*> container) {
324 	using WorkMode = Core::Settings::WorkMode;
325 
326 	const auto checkbox = [&](rpl::producer<QString> &&label, bool checked) {
327 		return object_ptr<Ui::Checkbox>(
328 			container,
329 			std::move(label),
330 			checked,
331 			st::settingsCheckbox);
332 	};
333 	const auto addCheckbox = [&](
334 			rpl::producer<QString> &&label,
335 			bool checked) {
336 		return container->add(
337 			checkbox(std::move(label), checked),
338 			st::settingsCheckboxPadding);
339 	};
340 	const auto addSlidingCheckbox = [&](
341 			rpl::producer<QString> &&label,
342 			bool checked) {
343 		return container->add(
344 			object_ptr<Ui::SlideWrap<Ui::Checkbox>>(
345 				container,
346 				checkbox(std::move(label), checked),
347 				st::settingsCheckboxPadding));
348 	};
349 
350 	if (Platform::TrayIconSupported()) {
351 		const auto trayEnabled = [] {
352 			const auto workMode = Core::App().settings().workMode();
353 			return (workMode == WorkMode::TrayOnly)
354 				|| (workMode == WorkMode::WindowAndTray);
355 		};
356 		const auto tray = addCheckbox(
357 			tr::lng_settings_workmode_tray(),
358 			trayEnabled());
359 
360 		const auto taskbarEnabled = [] {
361 			const auto workMode = Core::App().settings().workMode();
362 			return (workMode == WorkMode::WindowOnly)
363 				|| (workMode == WorkMode::WindowAndTray);
364 		};
365 		const auto taskbar = Platform::SkipTaskbarSupported()
366 			? addCheckbox(
367 				tr::lng_settings_workmode_window(),
368 				taskbarEnabled())
369 			: nullptr;
370 
371 		const auto updateWorkmode = [=] {
372 			const auto newMode = tray->checked()
373 				? ((!taskbar || taskbar->checked())
374 					? WorkMode::WindowAndTray
375 					: WorkMode::TrayOnly)
376 				: WorkMode::WindowOnly;
377 			if ((newMode == WorkMode::WindowAndTray
378 				|| newMode == WorkMode::TrayOnly)
379 				&& Core::App().settings().workMode() != newMode) {
380 				cSetSeenTrayTooltip(false);
381 			}
382 			Core::App().settings().setWorkMode(newMode);
383 			Local::writeSettings();
384 		};
385 
386 		tray->checkedChanges(
387 		) | rpl::filter([=](bool checked) {
388 			return (checked != trayEnabled());
389 		}) | rpl::start_with_next([=](bool checked) {
390 			if (!checked && taskbar && !taskbar->checked()) {
391 				taskbar->setChecked(true);
392 			} else {
393 				updateWorkmode();
394 			}
395 		}, tray->lifetime());
396 
397 		if (taskbar) {
398 			taskbar->checkedChanges(
399 			) | rpl::filter([=](bool checked) {
400 				return (checked != taskbarEnabled());
401 			}) | rpl::start_with_next([=](bool checked) {
402 				if (!checked && !tray->checked()) {
403 					tray->setChecked(true);
404 				} else {
405 					updateWorkmode();
406 				}
407 			}, taskbar->lifetime());
408 		}
409 	}
410 
411 	if (!Platform::IsMac()) {
412 		const auto closeToTaskbar = addSlidingCheckbox(
413 			tr::lng_settings_close_to_taskbar(),
414 			Core::App().settings().closeToTaskbar());
415 
416 		const auto closeToTaskbarShown = std::make_shared<rpl::variable<bool>>(false);
417 		Core::App().settings().workModeValue(
418 		) | rpl::start_with_next([=](WorkMode workMode) {
419 			*closeToTaskbarShown = (workMode == WorkMode::WindowOnly)
420 				|| !Platform::TrayIconSupported();
421 		}, closeToTaskbar->lifetime());
422 
423 		closeToTaskbar->toggleOn(closeToTaskbarShown->value());
424 		closeToTaskbar->entity()->checkedChanges(
425 		) | rpl::filter([=](bool checked) {
426 			return (checked != Core::App().settings().closeToTaskbar());
427 		}) | rpl::start_with_next([=](bool checked) {
428 			Core::App().settings().setCloseToTaskbar(checked);
429 			Local::writeSettings();
430 		}, closeToTaskbar->lifetime());
431 	}
432 
433 	if (Ui::Platform::NativeWindowFrameSupported()) {
434 		const auto nativeFrame = addCheckbox(
435 			tr::lng_settings_native_frame(),
436 			Core::App().settings().nativeWindowFrame());
437 
438 		nativeFrame->checkedChanges(
439 		) | rpl::filter([](bool checked) {
440 			return (checked != Core::App().settings().nativeWindowFrame());
441 		}) | rpl::start_with_next([=](bool checked) {
442 			Core::App().settings().setNativeWindowFrame(checked);
443 			Core::App().saveSettingsDelayed();
444 		}, nativeFrame->lifetime());
445 	}
446 
447 	if (Platform::AutostartSupported() && controller) {
448 		const auto minimizedToggled = [=] {
449 			return cStartMinimized()
450 				&& !controller->session().domain().local().hasLocalPasscode();
451 		};
452 
453 		const auto autostart = addCheckbox(
454 			tr::lng_settings_auto_start(),
455 			cAutoStart());
456 		const auto minimized = addSlidingCheckbox(
457 			tr::lng_settings_start_min(),
458 			minimizedToggled());
459 
460 		autostart->checkedChanges(
461 		) | rpl::filter([](bool checked) {
462 			return (checked != cAutoStart());
463 		}) | rpl::start_with_next([=](bool checked) {
464 			cSetAutoStart(checked);
465 			Platform::AutostartToggle(checked, crl::guard(autostart, [=](
466 					bool enabled) {
467 				autostart->setChecked(enabled);
468 				if (enabled || !minimized->entity()->checked()) {
469 					Local::writeSettings();
470 				} else {
471 					minimized->entity()->setChecked(false);
472 				}
473 			}));
474 		}, autostart->lifetime());
475 
476 		Platform::AutostartRequestStateFromSystem(crl::guard(
477 			controller,
478 			[=](bool enabled) { autostart->setChecked(enabled); }));
479 
480 		minimized->toggleOn(autostart->checkedValue());
481 		minimized->entity()->checkedChanges(
482 		) | rpl::filter([=](bool checked) {
483 			return (checked != minimizedToggled());
484 		}) | rpl::start_with_next([=](bool checked) {
485 			if (controller->session().domain().local().hasLocalPasscode()) {
486 				minimized->entity()->setChecked(false);
487 				controller->show(Box<Ui::InformBox>(
488 					tr::lng_error_start_minimized_passcoded(tr::now)));
489 			} else {
490 				cSetStartMinimized(checked);
491 				Local::writeSettings();
492 			}
493 		}, minimized->lifetime());
494 
495 		controller->session().domain().local().localPasscodeChanged(
496 		) | rpl::start_with_next([=] {
497 			minimized->entity()->setChecked(minimizedToggled());
498 		}, minimized->lifetime());
499 	}
500 
501 	if (Platform::IsWindows() && !Platform::IsWindowsStoreBuild()) {
502 		const auto sendto = addCheckbox(
503 			tr::lng_settings_add_sendto(),
504 			cSendToMenu());
505 
506 		sendto->checkedChanges(
507 		) | rpl::filter([](bool checked) {
508 			return (checked != cSendToMenu());
509 		}) | rpl::start_with_next([](bool checked) {
510 			cSetSendToMenu(checked);
511 			psSendToMenu(checked);
512 			Local::writeSettings();
513 		}, sendto->lifetime());
514 	}
515 }
516 
SetupSystemIntegrationOptions(not_null<Window::SessionController * > controller,not_null<Ui::VerticalLayout * > container)517 void SetupSystemIntegrationOptions(
518 		not_null<Window::SessionController*> controller,
519 		not_null<Ui::VerticalLayout*> container) {
520 	auto wrap = object_ptr<Ui::VerticalLayout>(container);
521 	SetupSystemIntegrationContent(controller, wrap.data());
522 	if (wrap->count() > 0) {
523 		container->add(object_ptr<Ui::OverrideMargins>(
524 			container,
525 			std::move(wrap)));
526 
527 		AddSkip(container, st::settingsCheckboxesSkip);
528 	}
529 }
530 
SetupAnimations(not_null<Ui::VerticalLayout * > container)531 void SetupAnimations(not_null<Ui::VerticalLayout*> container) {
532 	AddButton(
533 		container,
534 		tr::lng_settings_enable_animations(),
535 		st::settingsButton
536 	)->toggleOn(
537 		rpl::single(!anim::Disabled())
538 	)->toggledValue(
539 	) | rpl::filter([](bool enabled) {
540 		return (enabled == anim::Disabled());
541 	}) | rpl::start_with_next([](bool enabled) {
542 		anim::SetDisabled(!enabled);
543 		Local::writeSettings();
544 	}, container->lifetime());
545 }
546 
547 #ifdef Q_OS_WIN
SetupANGLE(not_null<Window::SessionController * > controller,not_null<Ui::VerticalLayout * > container)548 void SetupANGLE(
549 		not_null<Window::SessionController*> controller,
550 		not_null<Ui::VerticalLayout*> container) {
551 	using ANGLE = Ui::GL::ANGLE;
552 	const auto options = std::vector{
553 		tr::lng_settings_angle_backend_auto(tr::now),
554 		tr::lng_settings_angle_backend_d3d11(tr::now),
555 		tr::lng_settings_angle_backend_d3d9(tr::now),
556 		tr::lng_settings_angle_backend_d3d11on12(tr::now),
557 		tr::lng_settings_angle_backend_opengl(tr::now),
558 		tr::lng_settings_angle_backend_disabled(tr::now),
559 	};
560 	const auto backendIndex = [] {
561 		if (Core::App().settings().disableOpenGL()) {
562 			return 5;
563 		} else switch (Ui::GL::CurrentANGLE()) {
564 		case ANGLE::Auto: return 0;
565 		case ANGLE::D3D11: return 1;
566 		case ANGLE::D3D9: return 2;
567 		case ANGLE::D3D11on12: return 3;
568 		case ANGLE::OpenGL: return 4;
569 		}
570 		Unexpected("Ui::GL::CurrentANGLE value in SetupANGLE.");
571 	}();
572 	const auto button = AddButtonWithLabel(
573 		container,
574 		tr::lng_settings_angle_backend(),
575 		rpl::single(options[backendIndex]),
576 		st::settingsButton);
577 	button->addClickHandler([=] {
578 		controller->show(Box([=](not_null<Ui::GenericBox*> box) {
579 			const auto save = [=](int index) {
580 				if (index == backendIndex) {
581 					return;
582 				}
583 				const auto confirmed = crl::guard(button, [=] {
584 					const auto nowDisabled = (index == 5);
585 					if (!nowDisabled) {
586 						Ui::GL::ChangeANGLE([&] {
587 							switch (index) {
588 							case 0: return ANGLE::Auto;
589 							case 1: return ANGLE::D3D11;
590 							case 2: return ANGLE::D3D9;
591 							case 3: return ANGLE::D3D11on12;
592 							case 4: return ANGLE::OpenGL;
593 							}
594 							Unexpected("Index in SetupANGLE.");
595 						}());
596 					}
597 					const auto wasDisabled = (backendIndex == 5);
598 					if (nowDisabled != wasDisabled) {
599 						Core::App().settings().setDisableOpenGL(nowDisabled);
600 						Local::writeSettings();
601 					}
602 					App::restart();
603 				});
604 				controller->show(Box<Ui::ConfirmBox>(
605 					tr::lng_settings_need_restart(tr::now),
606 					tr::lng_settings_restart_now(tr::now),
607 					confirmed));
608 			};
609 			SingleChoiceBox(box, {
610 				.title = tr::lng_settings_angle_backend(),
611 				.options = options,
612 				.initialSelection = backendIndex,
613 				.callback = save,
614 			});
615 		}));
616 	});
617 }
618 #endif // Q_OS_WIN
619 
SetupOpenGL(not_null<Window::SessionController * > controller,not_null<Ui::VerticalLayout * > container)620 void SetupOpenGL(
621 		not_null<Window::SessionController*> controller,
622 		not_null<Ui::VerticalLayout*> container) {
623 	const auto toggles = container->lifetime().make_state<
624 		rpl::event_stream<bool>
625 	>();
626 	const auto button = AddButton(
627 		container,
628 		tr::lng_settings_enable_opengl(),
629 		st::settingsButton
630 	)->toggleOn(
631 		toggles->events_starting_with_copy(
632 			!Core::App().settings().disableOpenGL())
633 	);
634 	button->toggledValue(
635 	) | rpl::filter([](bool enabled) {
636 		return (enabled == Core::App().settings().disableOpenGL());
637 	}) | rpl::start_with_next([=](bool enabled) {
638 		const auto confirmed = crl::guard(button, [=] {
639 			Core::App().settings().setDisableOpenGL(!enabled);
640 			Local::writeSettings();
641 			App::restart();
642 		});
643 		const auto cancelled = crl::guard(button, [=] {
644 			toggles->fire(!enabled);
645 		});
646 		controller->show(Box<Ui::ConfirmBox>(
647 			tr::lng_settings_need_restart(tr::now),
648 			tr::lng_settings_restart_now(tr::now),
649 			confirmed,
650 			cancelled));
651 	}, container->lifetime());
652 }
653 
SetupPerformance(not_null<Window::SessionController * > controller,not_null<Ui::VerticalLayout * > container)654 void SetupPerformance(
655 		not_null<Window::SessionController*> controller,
656 		not_null<Ui::VerticalLayout*> container) {
657 	SetupAnimations(container);
658 #ifdef Q_OS_WIN
659 	SetupANGLE(controller, container);
660 #else // Q_OS_WIN
661 	if constexpr (!Platform::IsMac()) {
662 		SetupOpenGL(controller, container);
663 	}
664 #endif // Q_OS_WIN
665 }
666 
SetupSystemIntegration(not_null<Window::SessionController * > controller,not_null<Ui::VerticalLayout * > container,Fn<void (Type)> showOther)667 void SetupSystemIntegration(
668 		not_null<Window::SessionController*> controller,
669 		not_null<Ui::VerticalLayout*> container,
670 		Fn<void(Type)> showOther) {
671 	AddDivider(container);
672 	AddSkip(container);
673 	AddSubsectionTitle(container, tr::lng_settings_system_integration());
674 	AddButton(
675 		container,
676 		tr::lng_settings_section_call_settings(),
677 		st::settingsButton
678 	)->addClickHandler([=] {
679 		showOther(Type::Calls);
680 	});
681 	SetupSystemIntegrationOptions(controller, container);
682 	AddSkip(container);
683 }
684 
Advanced(QWidget * parent,not_null<Window::SessionController * > controller)685 Advanced::Advanced(
686 	QWidget *parent,
687 	not_null<Window::SessionController*> controller)
688 : Section(parent) {
689 	setupContent(controller);
690 }
691 
sectionShowOther()692 rpl::producer<Type> Advanced::sectionShowOther() {
693 	return _showOther.events();
694 }
695 
setupContent(not_null<Window::SessionController * > controller)696 void Advanced::setupContent(not_null<Window::SessionController*> controller) {
697 	const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
698 
699 	auto empty = true;
700 	const auto addDivider = [&] {
701 		if (empty) {
702 			empty = false;
703 		} else {
704 			AddDivider(content);
705 		}
706 	};
707 	const auto addUpdate = [&] {
708 		if (HasUpdate()) {
709 			addDivider();
710 			AddSkip(content);
711 			AddSubsectionTitle(content, tr::lng_settings_version_info());
712 			SetupUpdate(content);
713 			AddSkip(content);
714 		}
715 	};
716 	if (!cAutoUpdate()) {
717 		addUpdate();
718 	}
719 	addDivider();
720 	AddSkip(content);
721 	AddSubsectionTitle(content, tr::lng_settings_network_proxy());
722 	SetupConnectionType(
723 		&controller->window(),
724 		&controller->session().account(),
725 		content);
726 	AddSkip(content);
727 	SetupDataStorage(controller, content);
728 	SetupAutoDownload(controller, content);
729 	SetupSystemIntegration(controller, content, [=](Type type) {
730 		_showOther.fire_copy(type);
731 	});
732 
733 	AddDivider(content);
734 	AddSkip(content);
735 	AddSubsectionTitle(content, tr::lng_settings_performance());
736 	SetupPerformance(controller, content);
737 	AddSkip(content);
738 
739 	if (HasSystemSpellchecker()) {
740 		AddDivider(content);
741 		AddSkip(content);
742 		AddSubsectionTitle(content, tr::lng_settings_spellchecker());
743 		SetupSpellchecker(controller, content);
744 		AddSkip(content);
745 	}
746 
747 	if (cAutoUpdate()) {
748 		addUpdate();
749 	}
750 
751 	Ui::ResizeFitChild(this, content);
752 }
753 
754 } // namespace Settings
755