Просмотр исходного кода

frontend: Create AlignmentSelector widget

Warchamp7 6 месяцев назад
Родитель
Сommit
c01a9bea49

+ 11 - 0
frontend/OBSApp.cpp

@@ -144,6 +144,15 @@ UncleanLaunchAction handleUncleanShutdown(bool enableCrashUpload)
 
 	return launchAction;
 }
+
+QAccessibleInterface *alignmentSelectorFactory(const QString &classname, QObject *object)
+{
+	if (classname == QLatin1String("AlignmentSelector")) {
+		if (auto *w = qobject_cast<AlignmentSelector *>(object))
+			return new AccessibleAlignmentSelector(w);
+	}
+	return nullptr;
+}
 } // namespace
 
 QObject *CreateShortcutFilter()
@@ -1022,6 +1031,8 @@ void OBSApp::AppInit()
 {
 	ProfileScope("OBSApp::AppInit");
 
+	QAccessible::installFactory(alignmentSelectorFactory);
+
 	if (!MakeUserDirs())
 		throw "Failed to create required user directories";
 	if (!InitGlobalConfig())

+ 6 - 0
frontend/cmake/ui-components.cmake

@@ -12,6 +12,12 @@ target_sources(
   PRIVATE
     components/AbsoluteSlider.cpp
     components/AbsoluteSlider.hpp
+    components/AccessibleAlignmentCell.cpp
+    components/AccessibleAlignmentCell.hpp
+    components/AccessibleAlignmentSelector.cpp
+    components/AccessibleAlignmentSelector.hpp
+    components/AlignmentSelector.cpp
+    components/AlignmentSelector.hpp
     components/ApplicationAudioCaptureToolbar.cpp
     components/ApplicationAudioCaptureToolbar.hpp
     components/AudioCaptureToolbar.cpp

+ 71 - 0
frontend/components/AccessibleAlignmentCell.cpp

@@ -0,0 +1,71 @@
+/******************************************************************************
+    Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "AccessibleAlignmentCell.hpp"
+
+#include <OBSApp.hpp>
+
+using namespace std::string_view_literals;
+constexpr std::array indexToStrings = {
+	"Basic.TransformWindow.Alignment.TopLeft"sv,    "Basic.TransformWindow.Alignment.TopCenter"sv,
+	"Basic.TransformWindow.Alignment.TopRight"sv,   "Basic.TransformWindow.Alignment.CenterLeft"sv,
+	"Basic.TransformWindow.Alignment.Center"sv,     "Basic.TransformWindow.Alignment.CenterRight"sv,
+	"Basic.TransformWindow.Alignment.BottomLeft"sv, "Basic.TransformWindow.Alignment.BottomCenter"sv,
+	"Basic.TransformWindow.Alignment.BottomRight"sv};
+
+AccessibleAlignmentCell::AccessibleAlignmentCell(QAccessibleInterface *parent, AlignmentSelector *widget, int index)
+	: parent_(parent),
+	  widget(widget),
+	  index_(index)
+{
+}
+
+QRect AccessibleAlignmentCell::rect() const
+{
+	return widget->cellRect(index_);
+}
+
+QString AccessibleAlignmentCell::text(QAccessible::Text text) const
+{
+	if (text == QAccessible::Name || text == QAccessible::Value) {
+		return QString(indexToStrings[index_].data());
+	}
+	return QString();
+}
+
+QAccessible::State AccessibleAlignmentCell::state() const
+{
+	QAccessible::State state;
+	bool enabled = widget->isEnabled();
+
+	bool isSelectedCell = widget->currentIndex() == index_;
+	bool isFocusedCell = widget->focusedCell == index_;
+
+	state.disabled = !enabled;
+	state.focusable = enabled;
+	state.focused = widget->hasFocus() && isFocusedCell;
+	state.checkable = true;
+	state.checked = isSelectedCell;
+	state.selected = isSelectedCell;
+
+	return state;
+}
+
+QAccessible::Role AccessibleAlignmentCell::role() const
+{
+	return QAccessible::CheckBox;
+}

+ 51 - 0
frontend/components/AccessibleAlignmentCell.hpp

@@ -0,0 +1,51 @@
+/******************************************************************************
+    Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include <components/AlignmentSelector.hpp>
+
+#include <QAccessibleInterface>
+#include <QRect>
+
+class AlignmentSelector;
+
+class AccessibleAlignmentCell : public QAccessibleInterface {
+	QAccessibleInterface *parent_;
+	AlignmentSelector *widget;
+	int index_;
+
+public:
+	AccessibleAlignmentCell(QAccessibleInterface *parent, AlignmentSelector *widget, int index);
+
+	int index() const { return index_; }
+
+	QRect rect() const override;
+	QString text(QAccessible::Text t) const override;
+	QAccessible::State state() const override;
+	QAccessible::Role role() const override;
+
+	QObject *object() const override { return nullptr; }
+	QAccessibleInterface *child(int) const override { return nullptr; }
+	QAccessibleInterface *childAt(int, int) const override { return nullptr; }
+	int childCount() const override { return 0; }
+	int indexOfChild(const QAccessibleInterface *) const override { return -1; }
+	QAccessibleInterface *parent() const override { return parent_; }
+	QAccessibleInterface *focusChild() const override { return nullptr; }
+	bool isValid() const override { return widget != nullptr; }
+	void setText(QAccessible::Text, const QString &) override {}
+};

+ 126 - 0
frontend/components/AccessibleAlignmentSelector.cpp

@@ -0,0 +1,126 @@
+/******************************************************************************
+    Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "AccessibleAlignmentSelector.hpp"
+
+#include <OBSApp.hpp>
+
+AccessibleAlignmentSelector::AccessibleAlignmentSelector(AlignmentSelector *widget_)
+	: QAccessibleWidget(widget_, QAccessible::Grouping)
+{
+	for (int i = 0; i < cellCount; ++i) {
+		AccessibleAlignmentCell *cell = new AccessibleAlignmentCell(this, widget_, i);
+		QAccessible::registerAccessibleInterface(cell);
+		cellInterfaces.insert(i, QAccessible::uniqueId(cell));
+	}
+}
+
+AccessibleAlignmentSelector::~AccessibleAlignmentSelector()
+{
+	for (QAccessible::Id id : std::as_const(cellInterfaces)) {
+		QAccessible::deleteAccessibleInterface(id);
+	}
+}
+
+int AccessibleAlignmentSelector::childCount() const
+{
+	return cellCount;
+}
+
+QAccessibleInterface *AccessibleAlignmentSelector::child(int index) const
+{
+	if (QAccessible::Id id = cellInterfaces.value(index)) {
+		return QAccessible::accessibleInterface(id);
+	}
+
+	return nullptr;
+}
+
+int AccessibleAlignmentSelector::indexOfChild(const QAccessibleInterface *child) const
+{
+	if (!child) {
+		return -1;
+	}
+
+	QAccessible::Id id = QAccessible::uniqueId(const_cast<QAccessibleInterface *>(child));
+	return cellInterfaces.key(id, -1);
+}
+
+bool AccessibleAlignmentSelector::isValid() const
+{
+	return widget() != nullptr;
+}
+
+QAccessibleInterface *AccessibleAlignmentSelector::focusChild() const
+{
+	for (int i = 0; i < childCount(); ++i) {
+		if (child(i)->state().focused) {
+			return child(i);
+		}
+	}
+	return nullptr;
+}
+
+QRect AccessibleAlignmentSelector::rect() const
+{
+	return widget()->rect();
+}
+
+QString AccessibleAlignmentSelector::text(QAccessible::Text textType) const
+{
+	if (textType == QAccessible::Name) {
+		QString str = widget()->accessibleName();
+		if (str.isEmpty()) {
+			str = QTStr("Accessible.Widget.Name.AlignmentSelector");
+		}
+		return str;
+	}
+
+	if (textType == QAccessible::Value) {
+		return value().toString();
+	}
+
+	return QAccessibleWidget::text(textType);
+}
+
+QAccessible::Role AccessibleAlignmentSelector::role() const
+{
+	return QAccessible::Grouping;
+}
+
+QAccessible::State AccessibleAlignmentSelector::state() const
+{
+	QAccessible::State state;
+
+	state.focusable = true;
+	state.focused = widget()->hasFocus();
+	state.disabled = !widget()->isEnabled();
+	state.readOnly = false;
+
+	return state;
+}
+
+QVariant AccessibleAlignmentSelector::value() const
+{
+	for (int i = 0; i < childCount(); ++i) {
+		if (child(i)->state().checked) {
+			return child(i)->text(QAccessible::Name);
+		}
+	}
+
+	return QTStr("None");
+}

+ 47 - 0
frontend/components/AccessibleAlignmentSelector.hpp

@@ -0,0 +1,47 @@
+/******************************************************************************
+    Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include <components/AccessibleAlignmentCell.hpp>
+
+#include <QAccessible>
+#include <QAccessibleInterface>
+#include <QAccessibleWidget>
+
+class AlignmentSelector;
+
+class AccessibleAlignmentSelector : public QAccessibleWidget {
+	mutable QHash<int, QAccessible::Id> cellInterfaces{};
+	static constexpr int cellCount = 9;
+
+public:
+	explicit AccessibleAlignmentSelector(AlignmentSelector *widget);
+	~AccessibleAlignmentSelector();
+
+	QRect rect() const override;
+	QAccessible::Role role() const override;
+	QAccessible::State state() const override;
+	QString text(QAccessible::Text t) const override;
+	QAccessibleInterface *child(int index) const override;
+	int childCount() const override;
+	int indexOfChild(const QAccessibleInterface *child) const override;
+	bool isValid() const override;
+	QAccessibleInterface *focusChild() const override;
+
+	QVariant value() const;
+};

+ 324 - 0
frontend/components/AlignmentSelector.cpp

@@ -0,0 +1,324 @@
+/******************************************************************************
+    Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "AlignmentSelector.hpp"
+
+#include <util/base.h>
+
+#include <QAccessible>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QStyleOptionButton>
+
+AlignmentSelector::AlignmentSelector(QWidget *parent) : QWidget(parent)
+{
+	setFocusPolicy(Qt::StrongFocus);
+	setMouseTracking(true);
+	setAttribute(Qt::WA_Hover);
+}
+
+QSize AlignmentSelector::sizeHint() const
+{
+	int base = fontMetrics().height() * 2;
+	return QSize(base, base);
+}
+
+QSize AlignmentSelector::minimumSizeHint() const
+{
+	return QSize(16, 16);
+}
+
+Qt::Alignment AlignmentSelector::value() const
+{
+	return cellAlignment(selectedCell);
+}
+
+int AlignmentSelector::currentIndex() const
+{
+	return selectedCell;
+}
+
+void AlignmentSelector::setAlignment(Qt::Alignment value)
+{
+	alignment = value;
+}
+
+void AlignmentSelector::setCurrentIndex(int index)
+{
+	selectCell(index);
+}
+
+void AlignmentSelector::paintEvent(QPaintEvent *)
+{
+	QPainter painter(this);
+	QStyle *style = this->style();
+
+	int cellW = gridRect().width() / 3;
+	int cellH = gridRect().height() / 3;
+
+	for (int i = 0; i < 9; ++i) {
+		QRect rect = cellRect(i);
+		rect = rect.adjusted(0, 0, -1, -1);
+
+		QStyleOptionFrame frameOpt;
+		frameOpt.rect = rect;
+		frameOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
+		frameOpt.lineWidth = 1;
+		frameOpt.midLineWidth = 0;
+		if (i == hoveredCell) {
+			frameOpt.state |= QStyle::State_MouseOver;
+		}
+		if (i == selectedCell) {
+			frameOpt.state |= QStyle::State_On;
+		}
+		if (i == focusedCell && hasFocus()) {
+			frameOpt.state |= QStyle::State_HasFocus;
+		}
+
+		QStyleOptionButton radioOpt;
+		radioOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
+		radioOpt.rect = rect.adjusted(cellW / 6, cellH / 6, -cellW / 6, -cellH / 6);
+		if (i == hoveredCell) {
+			radioOpt.state |= QStyle::State_MouseOver;
+		}
+		if (i == selectedCell) {
+			radioOpt.state |= QStyle::State_On;
+		}
+
+		if (i == focusedCell && hasFocus()) {
+			radioOpt.state |= QStyle::State_HasFocus;
+		}
+		style->drawPrimitive(QStyle::PE_IndicatorRadioButton, &radioOpt, &painter, this);
+
+		style->drawPrimitive(QStyle::PE_Frame, &frameOpt, &painter, this);
+
+		if (i == focusedCell && hasFocus()) {
+			QStyleOptionFocusRect focusOpt;
+			focusOpt.initFrom(this);
+			focusOpt.rect = rect.adjusted(1, 1, -1, -1);
+			focusOpt.state = isEnabled() ? QStyle::State_Enabled : QStyle::State_None;
+			focusOpt.state |= QStyle::State_HasFocus;
+			style->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpt, &painter, this);
+		}
+	}
+}
+
+QRect AlignmentSelector::cellRect(int index) const
+{
+	int col = index % 3;
+	int row = index / 3;
+
+	QRect gridRect = this->gridRect();
+	int cellW = gridRect.width() / 3;
+	int cellH = gridRect.height() / 3;
+
+	return QRect(col * cellW + gridRect.left(), row * cellH + gridRect.top(), cellW, cellH);
+}
+
+Qt::Alignment AlignmentSelector::cellAlignment(int index) const
+{
+	Qt::Alignment hAlign;
+	Qt::Alignment vAlign;
+
+	switch (index % 3) {
+	case 0:
+		hAlign = Qt::AlignLeft;
+		break;
+	case 1:
+		hAlign = Qt::AlignHCenter;
+		break;
+	case 2:
+		hAlign = Qt::AlignRight;
+		break;
+	}
+
+	switch (index / 3) {
+	case 0:
+		vAlign = Qt::AlignTop;
+		break;
+	case 1:
+		vAlign = Qt::AlignVCenter;
+		break;
+	case 2:
+		vAlign = Qt::AlignBottom;
+		break;
+	}
+
+	return hAlign | vAlign;
+}
+
+void AlignmentSelector::leaveEvent(QEvent *)
+{
+	hoveredCell = -1;
+	update();
+}
+
+void AlignmentSelector::mouseMoveEvent(QMouseEvent *event)
+{
+	QRect grid = gridRect();
+	int cellW = grid.width() / 3;
+	int cellH = grid.height() / 3;
+
+	QPoint pos = event->position().toPoint();
+	if (!grid.contains(pos)) {
+		hoveredCell = -1;
+		return;
+	}
+
+	int col = (pos.x() - grid.left()) / cellW;
+	int row = (pos.y() - grid.top()) / cellH;
+	int cell = row * 3 + col;
+
+	if (hoveredCell != cell) {
+		hoveredCell = cell;
+		update();
+	}
+}
+
+void AlignmentSelector::mousePressEvent(QMouseEvent *event)
+{
+	QRect grid = gridRect();
+	int cellW = grid.width() / 3;
+	int cellH = grid.height() / 3;
+
+	QPoint pos = event->position().toPoint();
+	if (!grid.contains(pos)) {
+		return;
+	}
+
+	int col = (pos.x() - grid.left()) / cellW;
+	int row = (pos.y() - grid.top()) / cellH;
+	int cell = row * 3 + col;
+
+	selectCell(cell);
+}
+
+void AlignmentSelector::keyPressEvent(QKeyEvent *event)
+{
+	int moveX = 0;
+	int moveY = 0;
+
+	switch (event->key()) {
+	case Qt::Key_Left:
+		moveX = -1;
+		break;
+	case Qt::Key_Right:
+		moveX = 1;
+		break;
+	case Qt::Key_Up:
+		moveY = -1;
+		break;
+	case Qt::Key_Down:
+		moveY = 1;
+		break;
+	case Qt::Key_Space:
+	case Qt::Key_Return:
+	case Qt::Key_Enter:
+		selectCell(focusedCell);
+		return;
+	default:
+		QWidget::keyPressEvent(event);
+		return;
+	}
+
+	moveFocusedCell(moveX, moveY);
+}
+
+QRect AlignmentSelector::gridRect() const
+{
+	int side = std::min(width(), height());
+	int x = 0;
+	int y = 0;
+
+	if (alignment & Qt::AlignHCenter) {
+		x = (width() - side) / 2;
+	} else if (alignment & Qt::AlignRight) {
+		x = width() - side;
+	}
+
+	if (alignment & Qt::AlignVCenter) {
+		y = (height() - side) / 2;
+	} else if (alignment & Qt::AlignBottom) {
+		y = height() - side;
+	}
+
+	return QRect(x, y, side, side);
+}
+
+void AlignmentSelector::moveFocusedCell(int moveX, int moveY)
+{
+	int row = focusedCell / 3;
+	int col = focusedCell % 3;
+
+	row = std::clamp(row + moveY, 0, 2);
+	col = std::clamp(col + moveX, 0, 2);
+
+	int newCell = row * 3 + col;
+	setFocusedCell(newCell);
+}
+
+void AlignmentSelector::setFocusedCell(int cell)
+{
+	if (cell != focusedCell) {
+		focusedCell = cell;
+		update();
+
+		if (AccessibleAlignmentSelector *interface =
+			    dynamic_cast<AccessibleAlignmentSelector *>(QAccessible::queryAccessibleInterface(this))) {
+			if (QAccessibleInterface *child = interface->child(cell)) {
+				QAccessibleEvent event(child, QAccessible::Focus);
+				QAccessible::updateAccessibility(&event);
+			}
+		}
+	}
+}
+
+void AlignmentSelector::selectCell(int cell)
+{
+	setFocusedCell(cell);
+	if (cell != selectedCell) {
+		selectedCell = cell;
+
+		emit valueChanged(cellAlignment(cell));
+		emit currentIndexChanged(cell);
+	}
+	update();
+
+	if (AccessibleAlignmentSelector *interface =
+		    dynamic_cast<AccessibleAlignmentSelector *>(QAccessible::queryAccessibleInterface(this))) {
+		if (QAccessibleInterface *child = interface->child(cell)) {
+			QAccessible::State state;
+			state.checked = true;
+
+			QAccessibleStateChangeEvent event(child, state);
+			QAccessible::updateAccessibility(&event);
+		}
+	}
+}
+
+void AlignmentSelector::focusInEvent(QFocusEvent *)
+{
+	setFocusedCell(selectedCell);
+	update();
+}
+
+void AlignmentSelector::focusOutEvent(QFocusEvent *)
+{
+	hoveredCell = -1;
+	setFocusedCell(-1);
+	update();
+}

+ 71 - 0
frontend/components/AlignmentSelector.hpp

@@ -0,0 +1,71 @@
+/******************************************************************************
+    Copyright (C) 2025 by Taylor Giampaolo <[email protected]>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include <components/AccessibleAlignmentSelector.hpp>
+
+#include <QFocusEvent>
+#include <QKeyEvent>
+#include <QWidget>
+
+class AlignmentSelector : public QWidget {
+	Q_OBJECT
+
+	friend class AccessibleAlignmentSelector;
+	friend class AccessibleAlignmentCell;
+
+public:
+	explicit AlignmentSelector(QWidget *parent = nullptr);
+
+	QSize sizeHint() const override;
+	QSize minimumSizeHint() const override;
+	Qt::Alignment value() const;
+	int currentIndex() const;
+
+	void setAlignment(Qt::Alignment alignment);
+	void setCurrentIndex(int index);
+
+signals:
+	void valueChanged(Qt::Alignment value);
+	void currentIndexChanged(int value);
+
+protected:
+	QRect cellRect(int index) const;
+
+	void paintEvent(QPaintEvent *event) override;
+	void leaveEvent(QEvent *event) override;
+	void mouseMoveEvent(QMouseEvent *event) override;
+	void mousePressEvent(QMouseEvent *event) override;
+	void keyPressEvent(QKeyEvent *event) override;
+	void focusInEvent(QFocusEvent *event) override;
+	void focusOutEvent(QFocusEvent *event) override;
+
+private:
+	Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;
+
+	int hoveredCell = -1;
+	int focusedCell = 4;
+	int selectedCell = 4;
+
+	QRect gridRect() const;
+
+	void moveFocusedCell(int moveX, int moveY);
+	void setFocusedCell(int cell);
+	void selectCell(int cell);
+	Qt::Alignment cellAlignment(int index) const;
+};

+ 40 - 0
frontend/data/themes/Yami.obt

@@ -2513,3 +2513,43 @@ idian--RowFrame.hover .row-buddy {
 idian--RowFrame.hover idian--ExpandButton::indicator {
     border-color: var(--grey1);
 }
+
+AlignmentSelector {
+    border: 1px solid var(--input_border);
+    margin: 0px;
+    border-radius: 2px;
+}
+
+AlignmentSelector:focus,
+AlignmentSelector:hover {
+    border: 1px solid var(--white3);
+}
+
+AlignmentSelector:checked:hover {
+    border: 1px solid var(--primary_lighter);
+}
+
+AlignmentSelector::indicator {
+    margin: 0px;
+    background: transparent;    
+}
+
+AlignmentSelector::indicator:checked {
+    background: var(--primary);
+}
+
+AlignmentSelector::indicator:checked:focus {
+    background: var(--primary_light);
+}
+
+AlignmentSelector:disabled {
+    border: 1px solid var(--grey3);
+}
+
+AlignmentSelector::indicator:disabled {
+    background: var(--grey5);
+}
+
+AlignmentSelector::indicator:checked:disabled {
+    background: var(--grey3);
+}

+ 2 - 0
frontend/widgets/OBSBasic.hpp

@@ -21,6 +21,7 @@
 #include "OBSMainWindow.hpp"
 
 #include <OBSApp.hpp>
+#include <components/AccessibleAlignmentSelector.hpp>
 #include <oauth/Auth.hpp>
 #include <utility/BasicOutputHandler.hpp>
 #include <utility/OBSCanvas.hpp>
@@ -38,6 +39,7 @@
 #include <util/threading.h>
 #include <util/util.hpp>
 
+#include <QAccessible>
 #include <QSystemTrayIcon>
 
 #include <deque>