1
0

OBSBasic_Preview.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. /******************************************************************************
  2. Copyright (C) 2023 by Lain Bailey <[email protected]>
  3. Zachary Lund <[email protected]>
  4. Philippe Groarke <[email protected]>
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 2 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. ******************************************************************************/
  16. #include "OBSBasic.hpp"
  17. #include <utility/display-helpers.hpp>
  18. #include <widgets/OBSProjector.hpp>
  19. #include <qt-wrappers.hpp>
  20. #include <QColorDialog>
  21. #include <sstream>
  22. extern void undo_redo(const std::string &data);
  23. using namespace std;
  24. void OBSBasic::InitPrimitives()
  25. {
  26. ProfileScope("OBSBasic::InitPrimitives");
  27. obs_enter_graphics();
  28. gs_render_start(true);
  29. gs_vertex2f(0.0f, 0.0f);
  30. gs_vertex2f(0.0f, 1.0f);
  31. gs_vertex2f(1.0f, 0.0f);
  32. gs_vertex2f(1.0f, 1.0f);
  33. box = gs_render_save();
  34. gs_render_start(true);
  35. gs_vertex2f(0.0f, 0.0f);
  36. gs_vertex2f(0.0f, 1.0f);
  37. boxLeft = gs_render_save();
  38. gs_render_start(true);
  39. gs_vertex2f(0.0f, 0.0f);
  40. gs_vertex2f(1.0f, 0.0f);
  41. boxTop = gs_render_save();
  42. gs_render_start(true);
  43. gs_vertex2f(1.0f, 0.0f);
  44. gs_vertex2f(1.0f, 1.0f);
  45. boxRight = gs_render_save();
  46. gs_render_start(true);
  47. gs_vertex2f(0.0f, 1.0f);
  48. gs_vertex2f(1.0f, 1.0f);
  49. boxBottom = gs_render_save();
  50. gs_render_start(true);
  51. for (int i = 0; i <= 360; i += (360 / 20)) {
  52. float pos = RAD(float(i));
  53. gs_vertex2f(cosf(pos), sinf(pos));
  54. }
  55. circle = gs_render_save();
  56. InitSafeAreas(&actionSafeMargin, &graphicsSafeMargin, &fourByThreeSafeMargin, &leftLine, &topLine, &rightLine);
  57. obs_leave_graphics();
  58. }
  59. void OBSBasic::UpdatePreviewScalingMenu()
  60. {
  61. bool fixedScaling = ui->preview->IsFixedScaling();
  62. float scalingAmount = ui->preview->GetScalingAmount();
  63. if (!fixedScaling) {
  64. ui->actionScaleWindow->setChecked(true);
  65. ui->actionScaleCanvas->setChecked(false);
  66. ui->actionScaleOutput->setChecked(false);
  67. return;
  68. }
  69. obs_video_info ovi;
  70. obs_get_video_info(&ovi);
  71. ui->actionScaleWindow->setChecked(false);
  72. ui->actionScaleCanvas->setChecked(scalingAmount == 1.0f);
  73. ui->actionScaleOutput->setChecked(scalingAmount == float(ovi.output_width) / float(ovi.base_width));
  74. }
  75. void OBSBasic::DrawBackdrop(float cx, float cy)
  76. {
  77. if (!box)
  78. return;
  79. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "DrawBackdrop");
  80. gs_effect_t *solid = obs_get_base_effect(OBS_EFFECT_SOLID);
  81. gs_eparam_t *color = gs_effect_get_param_by_name(solid, "color");
  82. gs_technique_t *tech = gs_effect_get_technique(solid, "Solid");
  83. vec4 colorVal;
  84. vec4_set(&colorVal, 0.0f, 0.0f, 0.0f, 1.0f);
  85. gs_effect_set_vec4(color, &colorVal);
  86. gs_technique_begin(tech);
  87. gs_technique_begin_pass(tech, 0);
  88. gs_matrix_push();
  89. gs_matrix_identity();
  90. gs_matrix_scale3f(float(cx), float(cy), 1.0f);
  91. gs_load_vertexbuffer(box);
  92. gs_draw(GS_TRISTRIP, 0, 0);
  93. gs_matrix_pop();
  94. gs_technique_end_pass(tech);
  95. gs_technique_end(tech);
  96. gs_load_vertexbuffer(nullptr);
  97. GS_DEBUG_MARKER_END();
  98. }
  99. void OBSBasic::RenderMain(void *data, uint32_t, uint32_t)
  100. {
  101. GS_DEBUG_MARKER_BEGIN(GS_DEBUG_COLOR_DEFAULT, "RenderMain");
  102. OBSBasic *window = static_cast<OBSBasic *>(data);
  103. obs_video_info ovi;
  104. obs_get_video_info(&ovi);
  105. window->previewCX = int(window->previewScale * float(ovi.base_width));
  106. window->previewCY = int(window->previewScale * float(ovi.base_height));
  107. gs_viewport_push();
  108. gs_projection_push();
  109. obs_display_t *display = window->ui->preview->GetDisplay();
  110. uint32_t width, height;
  111. obs_display_size(display, &width, &height);
  112. float right = float(width) - window->previewX;
  113. float bottom = float(height) - window->previewY;
  114. gs_ortho(-window->previewX, right, -window->previewY, bottom, -100.0f, 100.0f);
  115. window->ui->preview->DrawOverflow();
  116. /* --------------------------------------- */
  117. gs_ortho(0.0f, float(ovi.base_width), 0.0f, float(ovi.base_height), -100.0f, 100.0f);
  118. gs_set_viewport(window->previewX, window->previewY, window->previewCX, window->previewCY);
  119. if (window->IsPreviewProgramMode()) {
  120. window->DrawBackdrop(float(ovi.base_width), float(ovi.base_height));
  121. OBSScene scene = window->GetCurrentScene();
  122. obs_source_t *source = obs_scene_get_source(scene);
  123. if (source)
  124. obs_source_video_render(source);
  125. } else {
  126. obs_render_main_texture_src_color_only();
  127. }
  128. gs_load_vertexbuffer(nullptr);
  129. /* --------------------------------------- */
  130. gs_ortho(-window->previewX, right, -window->previewY, bottom, -100.0f, 100.0f);
  131. gs_reset_viewport();
  132. uint32_t targetCX = window->previewCX;
  133. uint32_t targetCY = window->previewCY;
  134. if (window->drawSafeAreas) {
  135. RenderSafeAreas(window->actionSafeMargin, targetCX, targetCY);
  136. RenderSafeAreas(window->graphicsSafeMargin, targetCX, targetCY);
  137. RenderSafeAreas(window->fourByThreeSafeMargin, targetCX, targetCY);
  138. RenderSafeAreas(window->leftLine, targetCX, targetCY);
  139. RenderSafeAreas(window->topLine, targetCX, targetCY);
  140. RenderSafeAreas(window->rightLine, targetCX, targetCY);
  141. }
  142. window->ui->preview->DrawSceneEditing();
  143. if (window->drawSpacingHelpers)
  144. window->ui->preview->DrawSpacingHelpers();
  145. /* --------------------------------------- */
  146. gs_projection_pop();
  147. gs_viewport_pop();
  148. GS_DEBUG_MARKER_END();
  149. }
  150. void OBSBasic::ResizePreview(uint32_t cx, uint32_t cy)
  151. {
  152. QSize targetSize;
  153. bool isFixedScaling;
  154. obs_video_info ovi;
  155. /* resize preview panel to fix to the top section of the window */
  156. targetSize = GetPixelSize(ui->preview);
  157. isFixedScaling = ui->preview->IsFixedScaling();
  158. obs_get_video_info(&ovi);
  159. if (isFixedScaling) {
  160. previewScale = ui->preview->GetScalingAmount();
  161. ui->preview->ClampScrollingOffsets();
  162. GetCenterPosFromFixedScale(int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2,
  163. targetSize.height() - PREVIEW_EDGE_SIZE * 2, previewX, previewY,
  164. previewScale);
  165. previewX += ui->preview->GetScrollX();
  166. previewY += ui->preview->GetScrollY();
  167. } else {
  168. GetScaleAndCenterPos(int(cx), int(cy), targetSize.width() - PREVIEW_EDGE_SIZE * 2,
  169. targetSize.height() - PREVIEW_EDGE_SIZE * 2, previewX, previewY, previewScale);
  170. }
  171. ui->preview->SetScalingAmount(previewScale);
  172. previewX += float(PREVIEW_EDGE_SIZE);
  173. previewY += float(PREVIEW_EDGE_SIZE);
  174. }
  175. void OBSBasic::on_preview_customContextMenuRequested()
  176. {
  177. CreateSourcePopupMenu(GetTopSelectedSourceItem(), true);
  178. }
  179. void OBSBasic::on_previewDisabledWidget_customContextMenuRequested()
  180. {
  181. QMenu popup(this);
  182. delete previewProjectorMain;
  183. QAction *action = popup.addAction(QTStr("Basic.Main.PreviewConextMenu.Enable"), this, &OBSBasic::TogglePreview);
  184. action->setCheckable(true);
  185. action->setChecked(obs_display_enabled(ui->preview->GetDisplay()));
  186. previewProjectorMain = new QMenu(QTStr("Projector.Open.Preview"));
  187. AddProjectorMenuMonitors(previewProjectorMain, this, &OBSBasic::OpenPreviewProjector);
  188. previewProjectorMain->addSeparator();
  189. previewProjectorMain->addAction(QTStr("Projector.Window"), this, &OBSBasic::OpenPreviewWindow);
  190. popup.addMenu(previewProjectorMain);
  191. popup.exec(QCursor::pos());
  192. }
  193. void OBSBasic::EnablePreviewDisplay(bool enable)
  194. {
  195. obs_display_set_enabled(ui->preview->GetDisplay(), enable);
  196. ui->previewContainer->setVisible(enable);
  197. ui->previewDisabledWidget->setVisible(!enable);
  198. }
  199. void OBSBasic::TogglePreview()
  200. {
  201. previewEnabled = !previewEnabled;
  202. EnablePreviewDisplay(previewEnabled);
  203. }
  204. void OBSBasic::EnablePreview()
  205. {
  206. if (previewProgramMode)
  207. return;
  208. previewEnabled = true;
  209. EnablePreviewDisplay(true);
  210. }
  211. void OBSBasic::DisablePreview()
  212. {
  213. if (previewProgramMode)
  214. return;
  215. previewEnabled = false;
  216. EnablePreviewDisplay(false);
  217. }
  218. static bool nudge_callback(obs_scene_t *, obs_sceneitem_t *item, void *param)
  219. {
  220. if (obs_sceneitem_locked(item))
  221. return true;
  222. struct vec2 &offset = *static_cast<struct vec2 *>(param);
  223. struct vec2 pos;
  224. if (!obs_sceneitem_selected(item)) {
  225. if (obs_sceneitem_is_group(item)) {
  226. struct vec3 offset3;
  227. vec3_set(&offset3, offset.x, offset.y, 0.0f);
  228. struct matrix4 matrix;
  229. obs_sceneitem_get_draw_transform(item, &matrix);
  230. vec4_set(&matrix.t, 0.0f, 0.0f, 0.0f, 1.0f);
  231. matrix4_inv(&matrix, &matrix);
  232. vec3_transform(&offset3, &offset3, &matrix);
  233. struct vec2 new_offset;
  234. vec2_set(&new_offset, offset3.x, offset3.y);
  235. obs_sceneitem_group_enum_items(item, nudge_callback, &new_offset);
  236. }
  237. return true;
  238. }
  239. obs_sceneitem_get_pos(item, &pos);
  240. vec2_add(&pos, &pos, &offset);
  241. obs_sceneitem_set_pos(item, &pos);
  242. return true;
  243. }
  244. void OBSBasic::Nudge(int dist, MoveDir dir)
  245. {
  246. if (ui->preview->Locked())
  247. return;
  248. struct vec2 offset;
  249. vec2_set(&offset, 0.0f, 0.0f);
  250. switch (dir) {
  251. case MoveDir::Up:
  252. offset.y = (float)-dist;
  253. break;
  254. case MoveDir::Down:
  255. offset.y = (float)dist;
  256. break;
  257. case MoveDir::Left:
  258. offset.x = (float)-dist;
  259. break;
  260. case MoveDir::Right:
  261. offset.x = (float)dist;
  262. break;
  263. }
  264. if (!recent_nudge) {
  265. recent_nudge = true;
  266. OBSDataAutoRelease wrapper = obs_scene_save_transform_states(GetCurrentScene(), true);
  267. std::string undo_data(obs_data_get_json(wrapper));
  268. nudge_timer = new QTimer;
  269. QObject::connect(nudge_timer, &QTimer::timeout, [this, &recent_nudge = recent_nudge, undo_data]() {
  270. OBSDataAutoRelease rwrapper = obs_scene_save_transform_states(GetCurrentScene(), true);
  271. std::string redo_data(obs_data_get_json(rwrapper));
  272. undo_s.add_action(QTStr("Undo.Transform").arg(obs_source_get_name(GetCurrentSceneSource())),
  273. undo_redo, undo_redo, undo_data, redo_data);
  274. recent_nudge = false;
  275. });
  276. connect(nudge_timer, &QTimer::timeout, nudge_timer, &QTimer::deleteLater);
  277. nudge_timer->setSingleShot(true);
  278. }
  279. if (nudge_timer) {
  280. nudge_timer->stop();
  281. nudge_timer->start(1000);
  282. } else {
  283. blog(LOG_ERROR, "No nudge timer!");
  284. }
  285. obs_scene_enum_items(GetCurrentScene(), nudge_callback, &offset);
  286. }
  287. void OBSBasic::on_actionLockPreview_triggered()
  288. {
  289. ui->preview->ToggleLocked();
  290. ui->actionLockPreview->setChecked(ui->preview->Locked());
  291. }
  292. void OBSBasic::on_scalingMenu_aboutToShow()
  293. {
  294. obs_video_info ovi;
  295. obs_get_video_info(&ovi);
  296. QAction *action = ui->actionScaleCanvas;
  297. QString text = QTStr("Basic.MainMenu.Edit.Scale.Canvas");
  298. text = text.arg(QString::number(ovi.base_width), QString::number(ovi.base_height));
  299. action->setText(text);
  300. action = ui->actionScaleOutput;
  301. text = QTStr("Basic.MainMenu.Edit.Scale.Output");
  302. text = text.arg(QString::number(ovi.output_width), QString::number(ovi.output_height));
  303. action->setText(text);
  304. action->setVisible(!(ovi.output_width == ovi.base_width && ovi.output_height == ovi.base_height));
  305. UpdatePreviewScalingMenu();
  306. }
  307. void OBSBasic::setPreviewScalingWindow()
  308. {
  309. ui->preview->SetFixedScaling(false);
  310. ui->preview->ResetScrollingOffset();
  311. emit ui->preview->DisplayResized();
  312. }
  313. void OBSBasic::setPreviewScalingCanvas()
  314. {
  315. ui->preview->SetFixedScaling(true);
  316. ui->preview->SetScalingLevel(0);
  317. emit ui->preview->DisplayResized();
  318. }
  319. void OBSBasic::setPreviewScalingOutput()
  320. {
  321. obs_video_info ovi;
  322. obs_get_video_info(&ovi);
  323. ui->preview->SetFixedScaling(true);
  324. float scalingAmount = float(ovi.output_width) / float(ovi.base_width);
  325. // log base ZOOM_SENSITIVITY of x = log(x) / log(ZOOM_SENSITIVITY)
  326. int32_t approxScalingLevel = int32_t(round(log(scalingAmount) / log(ZOOM_SENSITIVITY)));
  327. ui->preview->SetScalingLevelAndAmount(approxScalingLevel, scalingAmount);
  328. emit ui->preview->DisplayResized();
  329. }
  330. static void ConfirmColor(SourceTree *sources, const QColor &color, QModelIndexList selectedItems)
  331. {
  332. for (int x = 0; x < selectedItems.count(); x++) {
  333. SourceTreeItem *treeItem = sources->GetItemWidget(selectedItems[x].row());
  334. treeItem->setStyleSheet("background: " + color.name(QColor::HexArgb));
  335. treeItem->style()->unpolish(treeItem);
  336. treeItem->style()->polish(treeItem);
  337. OBSSceneItem sceneItem = sources->Get(selectedItems[x].row());
  338. OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneItem);
  339. obs_data_set_int(privData, "color-preset", 1);
  340. obs_data_set_string(privData, "color", QT_TO_UTF8(color.name(QColor::HexArgb)));
  341. }
  342. }
  343. void OBSBasic::ColorChange()
  344. {
  345. QModelIndexList selectedItems = ui->sources->selectionModel()->selectedIndexes();
  346. QAction *action = qobject_cast<QAction *>(sender());
  347. QPushButton *colorButton = qobject_cast<QPushButton *>(sender());
  348. if (selectedItems.count() == 0)
  349. return;
  350. if (colorButton) {
  351. int preset = colorButton->property("bgColor").value<int>();
  352. for (int x = 0; x < selectedItems.count(); x++) {
  353. SourceTreeItem *treeItem = ui->sources->GetItemWidget(selectedItems[x].row());
  354. treeItem->setStyleSheet("");
  355. treeItem->setProperty("bgColor", preset);
  356. treeItem->style()->unpolish(treeItem);
  357. treeItem->style()->polish(treeItem);
  358. OBSSceneItem sceneItem = ui->sources->Get(selectedItems[x].row());
  359. OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneItem);
  360. obs_data_set_int(privData, "color-preset", preset + 1);
  361. obs_data_set_string(privData, "color", "");
  362. }
  363. for (int i = 1; i < 9; i++) {
  364. stringstream button;
  365. button << "preset" << i;
  366. QPushButton *cButton =
  367. colorButton->parentWidget()->findChild<QPushButton *>(button.str().c_str());
  368. cButton->setStyleSheet("border: 1px solid black");
  369. }
  370. colorButton->setStyleSheet("border: 2px solid black");
  371. } else if (action) {
  372. int preset = action->property("bgColor").value<int>();
  373. if (preset == 1) {
  374. OBSSceneItem curSceneItem = GetCurrentSceneItem();
  375. SourceTreeItem *curTreeItem = GetItemWidgetFromSceneItem(curSceneItem);
  376. OBSDataAutoRelease curPrivData = obs_sceneitem_get_private_settings(curSceneItem);
  377. int oldPreset = obs_data_get_int(curPrivData, "color-preset");
  378. const QString oldSheet = curTreeItem->styleSheet();
  379. auto liveChangeColor = [=](const QColor &color) {
  380. if (color.isValid()) {
  381. curTreeItem->setStyleSheet("background: " + color.name(QColor::HexArgb));
  382. }
  383. };
  384. auto changedColor = [=](const QColor &color) {
  385. if (color.isValid()) {
  386. ConfirmColor(ui->sources, color, selectedItems);
  387. }
  388. };
  389. auto rejected = [=]() {
  390. if (oldPreset == 1) {
  391. curTreeItem->setStyleSheet(oldSheet);
  392. curTreeItem->setProperty("bgColor", 0);
  393. } else if (oldPreset == 0) {
  394. curTreeItem->setStyleSheet("background: none");
  395. curTreeItem->setProperty("bgColor", 0);
  396. } else {
  397. curTreeItem->setStyleSheet("");
  398. curTreeItem->setProperty("bgColor", oldPreset - 1);
  399. }
  400. curTreeItem->style()->unpolish(curTreeItem);
  401. curTreeItem->style()->polish(curTreeItem);
  402. };
  403. QColorDialog::ColorDialogOptions options = QColorDialog::ShowAlphaChannel;
  404. const char *oldColor = obs_data_get_string(curPrivData, "color");
  405. const char *customColor = *oldColor != 0 ? oldColor : "#55FF0000";
  406. #ifdef __linux__
  407. // TODO: Revisit hang on Ubuntu with native dialog
  408. options |= QColorDialog::DontUseNativeDialog;
  409. #endif
  410. QColorDialog *colorDialog = new QColorDialog(this);
  411. colorDialog->setOptions(options);
  412. colorDialog->setCurrentColor(QColor(customColor));
  413. connect(colorDialog, &QColorDialog::currentColorChanged, liveChangeColor);
  414. connect(colorDialog, &QColorDialog::colorSelected, changedColor);
  415. connect(colorDialog, &QColorDialog::rejected, rejected);
  416. colorDialog->open();
  417. } else {
  418. for (int x = 0; x < selectedItems.count(); x++) {
  419. SourceTreeItem *treeItem = ui->sources->GetItemWidget(selectedItems[x].row());
  420. treeItem->setStyleSheet("background: none");
  421. treeItem->setProperty("bgColor", preset);
  422. treeItem->style()->unpolish(treeItem);
  423. treeItem->style()->polish(treeItem);
  424. OBSSceneItem sceneItem = ui->sources->Get(selectedItems[x].row());
  425. OBSDataAutoRelease privData = obs_sceneitem_get_private_settings(sceneItem);
  426. obs_data_set_int(privData, "color-preset", preset);
  427. obs_data_set_string(privData, "color", "");
  428. }
  429. }
  430. }
  431. }
  432. void OBSBasic::UpdateProjectorHideCursor()
  433. {
  434. for (size_t i = 0; i < projectors.size(); i++)
  435. projectors[i]->SetHideCursor();
  436. }
  437. void OBSBasic::UpdateProjectorAlwaysOnTop(bool top)
  438. {
  439. for (size_t i = 0; i < projectors.size(); i++)
  440. SetAlwaysOnTop(projectors[i], top);
  441. }
  442. void OBSBasic::ResetProjectors()
  443. {
  444. OBSDataArrayAutoRelease savedProjectorList = SaveProjectors();
  445. ClearProjectors();
  446. LoadSavedProjectors(savedProjectorList);
  447. }
  448. void OBSBasic::UpdatePreviewSafeAreas()
  449. {
  450. drawSafeAreas = config_get_bool(App()->GetUserConfig(), "BasicWindow", "ShowSafeAreas");
  451. }
  452. void OBSBasic::UpdatePreviewOverflowSettings()
  453. {
  454. bool hidden = config_get_bool(App()->GetUserConfig(), "BasicWindow", "OverflowHidden");
  455. bool select = config_get_bool(App()->GetUserConfig(), "BasicWindow", "OverflowSelectionHidden");
  456. bool always = config_get_bool(App()->GetUserConfig(), "BasicWindow", "OverflowAlwaysVisible");
  457. ui->preview->SetOverflowHidden(hidden);
  458. ui->preview->SetOverflowSelectionHidden(select);
  459. ui->preview->SetOverflowAlwaysVisible(always);
  460. }
  461. static inline QColor color_from_int(long long val)
  462. {
  463. return QColor(val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff, (val >> 24) & 0xff);
  464. }
  465. QColor OBSBasic::GetSelectionColor() const
  466. {
  467. if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) {
  468. return color_from_int(config_get_int(App()->GetUserConfig(), "Accessibility", "SelectRed"));
  469. } else {
  470. return QColor::fromRgb(255, 0, 0);
  471. }
  472. }
  473. QColor OBSBasic::GetCropColor() const
  474. {
  475. if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) {
  476. return color_from_int(config_get_int(App()->GetUserConfig(), "Accessibility", "SelectGreen"));
  477. } else {
  478. return QColor::fromRgb(0, 255, 0);
  479. }
  480. }
  481. QColor OBSBasic::GetHoverColor() const
  482. {
  483. if (config_get_bool(App()->GetUserConfig(), "Accessibility", "OverrideColors")) {
  484. return color_from_int(config_get_int(App()->GetUserConfig(), "Accessibility", "SelectBlue"));
  485. } else {
  486. return QColor::fromRgb(0, 127, 255);
  487. }
  488. }
  489. void OBSBasic::UpdatePreviewSpacingHelpers()
  490. {
  491. drawSpacingHelpers = config_get_bool(App()->GetUserConfig(), "BasicWindow", "SpacingHelpersEnabled");
  492. }
  493. float OBSBasic::GetDevicePixelRatio()
  494. {
  495. return dpi;
  496. }
  497. void OBSBasic::UpdatePreviewControls()
  498. {
  499. const int scalingLevel = ui->preview->GetScalingLevel();
  500. if (!ui->preview->IsFixedScaling()) {
  501. ui->previewXScrollBar->setRange(0, 0);
  502. ui->previewYScrollBar->setRange(0, 0);
  503. ui->actionPreviewResetZoom->setEnabled(false);
  504. return;
  505. }
  506. const bool minZoom = scalingLevel == MAX_SCALING_LEVEL;
  507. const bool maxZoom = scalingLevel == -MAX_SCALING_LEVEL;
  508. ui->actionPreviewZoomIn->setEnabled(!minZoom);
  509. ui->previewZoomInButton->setEnabled(!minZoom);
  510. ui->actionPreviewZoomOut->setEnabled(!maxZoom);
  511. ui->previewZoomOutButton->setEnabled(!maxZoom);
  512. ui->actionPreviewResetZoom->setEnabled(scalingLevel != 0);
  513. }
  514. void OBSBasic::PreviewScalingModeChanged(int value)
  515. {
  516. switch (value) {
  517. case 0:
  518. setPreviewScalingWindow();
  519. break;
  520. case 1:
  521. setPreviewScalingCanvas();
  522. break;
  523. case 2:
  524. setPreviewScalingOutput();
  525. break;
  526. };
  527. }