From 8ab25be54153c15520059e6f80b897ff269ffe49 Mon Sep 17 00:00:00 2001 From: Nikita Bazulin Date: Wed, 2 Oct 2024 15:58:27 +0300 Subject: [PATCH] [Packages] AudioControl: Add AppVM names support Signed-off-by: Nikita Bazulin --- packages/ghaf-audio-control/src/app/main.cpp | 35 +++++++++++++++---- .../ghaf-audio-control/src/lib/CMakeLists.txt | 2 ++ .../lib/include/GhafAudioControl/AppList.hpp | 3 ++ .../include/GhafAudioControl/AudioControl.hpp | 2 +- .../Backends/PulseAudio/GeneralDevice.hpp | 5 +++ .../Backends/PulseAudio/SinkInput.hpp | 5 +++ .../include/GhafAudioControl/utils/Debug.hpp | 14 ++++++++ .../include/GhafAudioControl/utils/Logger.hpp | 19 +++------- .../GhafAudioControl/widgets/AppVmWidget.hpp | 8 ++++- .../src/lib/src/AppList.cpp | 26 +++++++++++--- .../src/lib/src/AudioControl.cpp | 20 ++++++++--- .../PulseAudio/AudioControlBackend.cpp | 3 +- .../src/Backends/PulseAudio/GeneralDevide.cpp | 9 +++++ .../src/lib/src/utils/Debug.cpp | 29 +++++++++++++++ .../src/lib/src/utils/Logger.cpp | 26 ++++++++++++-- .../src/lib/src/widgets/AppVmWidget.cpp | 31 +++++++++++++--- .../src/lib/src/widgets/DeviceWidget.cpp | 10 +++--- 17 files changed, 202 insertions(+), 45 deletions(-) create mode 100644 packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Debug.hpp create mode 100644 packages/ghaf-audio-control/src/lib/src/utils/Debug.cpp diff --git a/packages/ghaf-audio-control/src/app/main.cpp b/packages/ghaf-audio-control/src/app/main.cpp index 9f5711a..ac6c074 100644 --- a/packages/ghaf-audio-control/src/app/main.cpp +++ b/packages/ghaf-audio-control/src/app/main.cpp @@ -5,11 +5,12 @@ #include #include +#include #include -#include - +#include #include +#include #include @@ -18,13 +19,26 @@ using namespace ghaf::AudioControl; namespace { -int GtkClient(std::string pulseAudioServerAddress) +std::vector GetAppVmsList(const std::string& appVms) { - auto app = Gtk::Application::create(); + std::vector result; - AudioControl audioControl{std::make_unique(std::move(pulseAudioServerAddress))}; + std::istringstream iss(appVms); + std::string buf; + + while (getline(iss, buf, ',')) + result.push_back(buf); + + return result; +} +int GtkClient(const std::string& pulseAudioServerAddress, const std::string& appVms) +{ + auto app = Gtk::Application::create(); Gtk::ApplicationWindow window; + + AudioControl audioControl{std::make_unique(pulseAudioServerAddress), GetAppVmsList(appVms)}; + window.add(audioControl); window.show_all(); @@ -35,14 +49,21 @@ int GtkClient(std::string pulseAudioServerAddress) int main(int argc, char** argv) { + pthread_setname_np(pthread_self(), "main"); + Glib::ustring pulseServerAddress; Glib::OptionEntry pulseServerOption; - pulseServerOption.set_long_name("pulseaudio_server"); pulseServerOption.set_description("PulseAudio server address"); + Glib::ustring appVms; + Glib::OptionEntry appVmsOption; + appVmsOption.set_long_name("app_vms"); + appVmsOption.set_description("AppVMs list"); + Glib::OptionGroup options("Main", "Main"); options.add_entry(pulseServerOption, pulseServerAddress); + options.add_entry(appVmsOption, appVms); Glib::OptionContext context("Application Options"); context.set_main_group(options); @@ -60,5 +81,5 @@ int main(int argc, char** argv) return 1; } - return GtkClient(pulseServerAddress); + return GtkClient(pulseServerAddress, appVms); } diff --git a/packages/ghaf-audio-control/src/lib/CMakeLists.txt b/packages/ghaf-audio-control/src/lib/CMakeLists.txt index ad04e9d..3543f62 100644 --- a/packages/ghaf-audio-control/src/lib/CMakeLists.txt +++ b/packages/ghaf-audio-control/src/lib/CMakeLists.txt @@ -30,6 +30,7 @@ PRIVATE src/models/DeviceModel.cpp src/utils/ConnectionContainer.cpp + src/utils/Debug.cpp src/utils/Logger.cpp src/widgets/AppVmWidget.cpp @@ -60,6 +61,7 @@ PUBLIC include/GhafAudioControl/models/DeviceModel.hpp include/GhafAudioControl/utils/ConnectionContainer.hpp + include/GhafAudioControl/utils/Debug.hpp include/GhafAudioControl/utils/Logger.hpp include/GhafAudioControl/utils/RaiiWrap.hpp include/GhafAudioControl/utils/ScopeExit.hpp diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AppList.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AppList.hpp index 74cf4a5..f1272fd 100644 --- a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AppList.hpp +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AppList.hpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,7 @@ class AppList final : public Gtk::Box public: AppList(); + void addVm(std::string appVmName); void addDevice(IAudioControlBackend::ISinkInput::Ptr device); void removeAllApps(); @@ -29,6 +31,7 @@ class AppList final : public Gtk::Box private: Gtk::ListBox m_listBox; + Glib::RefPtr m_cssProvider; Glib::RefPtr> m_appsModel; std::map>> m_appsBindings; }; diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AudioControl.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AudioControl.hpp index 8ac4c0a..dfa1004 100644 --- a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AudioControl.hpp +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/AudioControl.hpp @@ -21,7 +21,7 @@ namespace ghaf::AudioControl class AudioControl final : public Gtk::Box { public: - explicit AudioControl(std::unique_ptr backend); + AudioControl(std::unique_ptr backend, const std::vector& appVmsList); ~AudioControl() override = default; AudioControl(AudioControl&) = delete; diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/GeneralDevice.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/GeneralDevice.hpp index a34daa9..a51ab39 100644 --- a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/GeneralDevice.hpp +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/GeneralDevice.hpp @@ -50,6 +50,8 @@ class GeneralDeviceImpl final [[nodiscard]] pa_volume_t getPulseVolume() const; [[nodiscard]] pa_cvolume getPulseChannelVolume() const noexcept; + [[nodiscard]] std::optional getAppVmName() const noexcept; + [[nodiscard]] std::string getName() const; [[nodiscard]] std::string getDescription() const; @@ -78,6 +80,9 @@ class GeneralDeviceImpl final bool m_isDeleted = false; bool m_isEnabled = false; + + std::optional m_appVmName; + std::string m_name; std::string m_description; diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/SinkInput.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/SinkInput.hpp index a7e35c8..624476d 100644 --- a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/SinkInput.hpp +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/Backends/PulseAudio/SinkInput.hpp @@ -62,6 +62,11 @@ class SinkInput final : public IAudioControlBackend::ISinkInput return m_device.getDescription(); } + std::optional getAppVmName() const + { + return m_device.getAppVmName(); + } + void update(const pa_sink_input_info& info); void markDeleted(); diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Debug.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Debug.hpp new file mode 100644 index 0000000..ad8ab66 --- /dev/null +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Debug.hpp @@ -0,0 +1,14 @@ +/* + * Copyright 2022-2024 TII (SSRC) and the Ghaf contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace ghaf::AudioControl +{ + +void MarkUiTreadStarted(); +void CheckUiThread(); + +} // namespace ghaf::AudioControl diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Logger.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Logger.hpp index 67635b3..3881433 100644 --- a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Logger.hpp +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/utils/Logger.hpp @@ -13,23 +13,12 @@ namespace ghaf::AudioControl class Logger { public: - static void debug(std::string_view message) - { - log(message); - } - - static void error(std::string_view message) - { - log(message); - } - - static void info(std::string_view message) - { - log(message); - } + static void debug(std::string_view message); + static void error(std::string_view message); + static void info(std::string_view message); private: - static void log(std::string_view message); + static void log(std::string_view message, std::string_view logLevel); Logger(); }; diff --git a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/widgets/AppVmWidget.hpp b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/widgets/AppVmWidget.hpp index d138936..2be22dd 100644 --- a/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/widgets/AppVmWidget.hpp +++ b/packages/ghaf-audio-control/src/lib/include/GhafAudioControl/widgets/AppVmWidget.hpp @@ -24,14 +24,20 @@ class AppVmWidget final : public Gtk::Box void reveal(bool reveal = true); +private: + void onDeviceChange(guint position, guint removed, guint added); + private: Glib::RefPtr m_model; + Gtk::Box m_revealerBox; Gtk::ListBox m_listBox; + + Gtk::Label m_emptyListLabel; Gtk::Button m_appNameButton; Gtk::Revealer m_revealer; - sigc::connection m_connection; + ConnectionContainer m_connections; }; } // namespace ghaf::AudioControl diff --git a/packages/ghaf-audio-control/src/lib/src/AppList.cpp b/packages/ghaf-audio-control/src/lib/src/AppList.cpp index da5ea2f..5bf934d 100644 --- a/packages/ghaf-audio-control/src/lib/src/AppList.cpp +++ b/packages/ghaf-audio-control/src/lib/src/AppList.cpp @@ -4,9 +4,10 @@ */ #include -#include - +#include +#include #include +#include #include #include @@ -36,6 +37,12 @@ std::optional GetIndexByAppId(const Glib::RefPtr(device)) + { + if (const auto appVmName = sinkInput->getAppVmName()) + return *appVmName; + } + return "Other"; } @@ -61,6 +68,7 @@ Gtk::Widget* CreateWidgetsForApp(const Glib::RefPtr& appVmModelPtr AppList::AppList() : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) + , m_cssProvider(Gtk::CssProvider::create()) , m_appsModel(Gio::ListStore::create()) { m_listBox.bind_model(m_appsModel, &CreateWidgetsForApp); @@ -70,6 +78,14 @@ AppList::AppList() pack_start(m_listBox, Gtk::PACK_EXPAND_WIDGET); } +void AppList::addVm(std::string appVmName) +{ + if (const auto index = GetIndexByAppId(m_appsModel, appVmName)) + return; + + m_appsModel->append(AppVmModel::create(std::move(appVmName))); +} + void AppList::addDevice(IAudioControlBackend::ISinkInput::Ptr device) { const std::string appName = GetAppNameFromSinkInput(device); @@ -81,10 +97,12 @@ void AppList::addDevice(IAudioControlBackend::ISinkInput::Ptr device) Logger::error(std::format("AppList::addDevice: add new app with name: {}", appName)); auto appVmModel = AppVmModel::create(appName); - appVmModel->addSinkInput(std::move(device)); - m_appsModel->append(appVmModel); + + appVmModel->addSinkInput(std::move(device)); } + + show_all_children(true); } void AppList::removeAllApps() diff --git a/packages/ghaf-audio-control/src/lib/src/AudioControl.cpp b/packages/ghaf-audio-control/src/lib/src/AudioControl.cpp index 7cab247..53d9a0d 100644 --- a/packages/ghaf-audio-control/src/lib/src/AudioControl.cpp +++ b/packages/ghaf-audio-control/src/lib/src/AudioControl.cpp @@ -21,6 +21,11 @@ namespace ghaf::AudioControl { +constexpr auto CssStyle = "button#AppVmNameButton { background-color: transparent; border: none; font-weight: bold; }" + "box#DeviceWidget { background: #e6e6e6; border-radius: 15px; }" + "label#EmptyListName { background: #e6e6e6; border-radius: 15px; min-height: 40px; }" + "*:selected { background-color: transparent; color: inherit; box-shadow: none; outline: none; }"; + template void OnPulseDeviceChanged(IAudioControlBackend::EventType eventType, IndexT index, DevicePtrT device, AppList& appList) { @@ -64,10 +69,13 @@ void OnPulseDeviceChanged(IAudioControlBackend::EventType eventType, IndexT inde } } -AudioControl::AudioControl(std::unique_ptr backend) +AudioControl::AudioControl(std::unique_ptr backend, const std::vector& appVmsList) : Gtk::Box(Gtk::ORIENTATION_HORIZONTAL) , m_audioControl(std::move(backend)) { + for (const auto& appVm : appVmsList) + m_appList.addVm(appVm); + init(); } @@ -91,34 +99,36 @@ void AudioControl::init() { onPulseError("No audio backend"); } + + auto cssProvider = Gtk::CssProvider::create(); + cssProvider->load_from_data(CssStyle); + + auto style_context = Gtk::StyleContext::create(); + style_context->add_provider_for_screen(Gdk::Screen::get_default(), cssProvider, GTK_STYLE_PROVIDER_PRIORITY_USER); } void AudioControl::onPulseSinksChanged(IAudioControlBackend::EventType eventType, IAudioControlBackend::Sinks::IndexT extIndex, IAudioControlBackend::Sinks::PtrT sink) { OnPulseDeviceChanged(eventType, extIndex, std::move(sink), m_appList); - show_all_children(); } void AudioControl::onPulseSourcesChanged(IAudioControlBackend::EventType eventType, IAudioControlBackend::Sources::IndexT extIndex, IAudioControlBackend::Sources::PtrT source) { OnPulseDeviceChanged(eventType, extIndex, std::move(source), m_appList); - show_all_children(); } void AudioControl::onPulseSinkInputsChanged(IAudioControlBackend::EventType eventType, IAudioControlBackend::SinkInputs::IndexT extIndex, IAudioControlBackend::SinkInputs::PtrT sinkInput) { OnPulseDeviceChanged(eventType, extIndex, std::move(sinkInput), m_appList); - show_all_children(); } void AudioControl::onPulseSourcesOutputsChanged(IAudioControlBackend::EventType eventType, IAudioControlBackend::SourceOutputs::IndexT extIndex, IAudioControlBackend::SourceOutputs::PtrT sourceOutput) { OnPulseDeviceChanged(eventType, extIndex, std::move(sourceOutput), m_appList); - show_all_children(); } void AudioControl::onPulseError(std::string_view error) diff --git a/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/AudioControlBackend.cpp b/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/AudioControlBackend.cpp index e7949c9..6833d7c 100644 --- a/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/AudioControlBackend.cpp +++ b/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/AudioControlBackend.cpp @@ -37,9 +37,8 @@ void OnPulseDeviceInfo(const InfoT& info, IAudioControlBackend::SignalMapsecond; - Logger::debug(std::format("Updating... Before: {}", device.toString())); map.update(*deviceIt, [&info](IDeviceT& device) { dynamic_cast(device).update(info); }); - Logger::debug(std::format("Updating... After: {}", device.toString())); + Logger::debug(std::format("Updating... {}", device.toString())); } else { diff --git a/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/GeneralDevide.cpp b/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/GeneralDevide.cpp index a432605..5a9d28d 100644 --- a/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/GeneralDevide.cpp +++ b/packages/ghaf-audio-control/src/lib/src/Backends/PulseAudio/GeneralDevide.cpp @@ -12,6 +12,8 @@ namespace ghaf::AudioControl::Backend::PulseAudio { +constexpr auto PropertyAppVmName = "application.process.host"; + GeneralDeviceImpl::GeneralDeviceImpl(const pa_sink_info& info, pa_context& context) : m_index(info.index) , m_cardIndex(info.card) @@ -39,6 +41,7 @@ GeneralDeviceImpl::GeneralDeviceImpl(const pa_source_info& info, pa_context& con GeneralDeviceImpl::GeneralDeviceImpl(const pa_sink_input_info& info, pa_context& context) : m_index(info.index) , m_cardIndex(0) + , m_appVmName(pa_proplist_gets(info.proplist, PropertyAppVmName)) , m_name(info.name) , m_context(context) , m_channel_map(info.channel_map) @@ -99,6 +102,12 @@ GeneralDeviceImpl::GeneralDeviceImpl(const pa_source_output_info& info, pa_conte return m_volume; } +std::optional GeneralDeviceImpl::getAppVmName() const noexcept +{ + const std::lock_guard l{m_mutex}; + return m_appVmName; +} + [[nodiscard]] std::string GeneralDeviceImpl::getName() const { const std::lock_guard l{m_mutex}; diff --git a/packages/ghaf-audio-control/src/lib/src/utils/Debug.cpp b/packages/ghaf-audio-control/src/lib/src/utils/Debug.cpp new file mode 100644 index 0000000..8082733 --- /dev/null +++ b/packages/ghaf-audio-control/src/lib/src/utils/Debug.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2022-2024 TII (SSRC) and the Ghaf contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include + +#include + +namespace ghaf::AudioControl +{ + +std::atomic_bool UiThreadStarted = false; + +void MarkUiTreadStarted() +{ + UiThreadStarted = true; +} + +void CheckUiThread() +{ + if (UiThreadStarted) + assert(g_main_context_is_owner(nullptr) && "Accessing widgets out of the UI thread!"); +} + +} // namespace ghaf::AudioControl diff --git a/packages/ghaf-audio-control/src/lib/src/utils/Logger.cpp b/packages/ghaf-audio-control/src/lib/src/utils/Logger.cpp index 7cd31ed..9837b9b 100644 --- a/packages/ghaf-audio-control/src/lib/src/utils/Logger.cpp +++ b/packages/ghaf-audio-control/src/lib/src/utils/Logger.cpp @@ -5,14 +5,36 @@ #include +#include +#include #include namespace ghaf::AudioControl { -void Logger::log(std::string_view message) +void Logger::debug(std::string_view message) { - std::cerr << message << '\n'; + log(message, "debug"); +} + +void Logger::error(std::string_view message) +{ + log(message, "error"); +} + +void Logger::info(std::string_view message) +{ + log(message, "info"); +} + +void Logger::log(std::string_view message, std::string_view logLevel) +{ + const std::chrono::time_point timeNow = std::chrono::system_clock::now(); + + if (logLevel == "error") + std::cerr << "\033[31m"; + + std::cerr << std::format("[{}] [{:5}] {}", timeNow, logLevel, message) << "\033[0m" << '\n'; } } // namespace ghaf::AudioControl diff --git a/packages/ghaf-audio-control/src/lib/src/widgets/AppVmWidget.cpp b/packages/ghaf-audio-control/src/lib/src/widgets/AppVmWidget.cpp index d5daa63..5ff8519 100644 --- a/packages/ghaf-audio-control/src/lib/src/widgets/AppVmWidget.cpp +++ b/packages/ghaf-audio-control/src/lib/src/widgets/AppVmWidget.cpp @@ -5,8 +5,13 @@ #include +#include #include +#include + +#include + namespace ghaf::AudioControl { @@ -39,24 +44,42 @@ Gtk::Widget* CreateDeviceWidget(const Glib::RefPtr& deviceModelPtr AppVmWidget::AppVmWidget(Glib::RefPtr model) : Gtk::Box(Gtk::Orientation::ORIENTATION_VERTICAL) , m_model(std::move(model)) + , m_revealerBox(Gtk::Orientation::ORIENTATION_VERTICAL) + , m_emptyListLabel("No streams from the AppVM") , m_appNameButton("AppVm: " + m_model->getAppNameProperty()) - , m_connection(m_appNameButton.signal_clicked().connect([this] { m_revealer.set_reveal_child(!m_revealer.get_reveal_child()); })) + , m_connections{m_appNameButton.signal_clicked().connect([this] { m_revealer.set_reveal_child(!m_revealer.get_reveal_child()); }), + m_model->getDeviceModels()->signal_items_changed().connect(sigc::mem_fun(*this, &AppVmWidget::onDeviceChange))} { - m_revealer.add(m_listBox); + m_emptyListLabel.set_name("EmptyListName"); + m_emptyListLabel.set_halign(Gtk::Align::ALIGN_FILL); + + m_revealerBox.pack_start(m_emptyListLabel); + m_revealerBox.pack_end(m_listBox); + + m_revealer.add(m_revealerBox); m_revealer.set_transition_type(RevealerTransitionType); m_revealer.set_transition_duration(RevealerAnimationTimeMs); m_revealer.set_reveal_child(); - add(m_appNameButton); - add(m_revealer); + m_appNameButton.set_name("AppVmNameButton"); + m_appNameButton.set_halign(Gtk::Align::ALIGN_START); + + pack_start(m_appNameButton); + pack_start(m_revealer); m_listBox.bind_model(m_model->getDeviceModels(), &CreateDeviceWidget); m_listBox.set_can_focus(false); m_listBox.set_selection_mode(Gtk::SelectionMode::SELECTION_SINGLE); } +void AppVmWidget::onDeviceChange([[maybe_unused]] guint position, [[maybe_unused]] guint removed, [[maybe_unused]] guint added) +{ + Glib::signal_idle().connect_once([this]() { m_emptyListLabel.set_visible(m_model->getDeviceModels()->get_n_items() == 0); }, Glib::PRIORITY_DEFAULT); +} + void AppVmWidget::reveal(bool reveal) { + CheckUiThread(); m_revealer.set_reveal_child(reveal); } diff --git a/packages/ghaf-audio-control/src/lib/src/widgets/DeviceWidget.cpp b/packages/ghaf-audio-control/src/lib/src/widgets/DeviceWidget.cpp index 2a7f7a4..6df8bd7 100644 --- a/packages/ghaf-audio-control/src/lib/src/widgets/DeviceWidget.cpp +++ b/packages/ghaf-audio-control/src/lib/src/widgets/DeviceWidget.cpp @@ -3,6 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include "GhafAudioControl/utils/Debug.hpp" #include #include @@ -21,6 +22,8 @@ constexpr auto ScaleUpperLimit = 100.0; constexpr auto DeviceWidgetSpacing = 5; +constexpr auto NameLabelLeftMargin = 20; + auto Bind(const auto& appProp, const auto& widgetProp, bool readonly = false) { auto flag = Glib::BindingFlags::BINDING_SYNC_CREATE; @@ -58,9 +61,10 @@ DeviceWidget::DeviceWidget(DeviceModel::Ptr model) widget.set_hexpand(false); widget.set_vexpand(false); widget.set_halign(Gtk::Align::ALIGN_START); - widget.set_valign(Gtk::Align::ALIGN_END); + widget.set_valign(Gtk::Align::ALIGN_CENTER); }; + set_name("DeviceWidget"); set_homogeneous(true); set_spacing(DeviceWidgetSpacing); @@ -72,9 +76,7 @@ DeviceWidget::DeviceWidget(DeviceModel::Ptr model) add(*m_switch); add(*m_scale); - m_nameLabel->set_can_focus(false); - m_nameLabel->set_selectable(false); - m_nameLabel->set_focus_on_click(false); + m_nameLabel->set_margin_left(NameLabelLeftMargin); m_switch->set_halign(Gtk::Align::ALIGN_END); m_scale->set_halign(Gtk::Align::ALIGN_END);