uisupport: Provide helpers for dealing with widget changes
[quassel.git] / src / uisupport / widgethelpers.h
diff --git a/src/uisupport/widgethelpers.h b/src/uisupport/widgethelpers.h
new file mode 100644 (file)
index 0000000..22f9f9c
--- /dev/null
@@ -0,0 +1,136 @@
+/***************************************************************************
+ *   Copyright (C) 2005-2018 by the Quassel Project                        *
+ *   devel@quassel-irc.org                                                 *
+ *                                                                         *
+ *   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) version 3.                                           *
+ *                                                                         *
+ *   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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
+ ***************************************************************************/
+
+#pragma once
+
+#include <algorithm>
+#include <initializer_list>
+#include <tuple>
+#include <utility>
+
+#include <QAbstractButton>
+#include <QComboBox>
+#include <QDoubleSpinBox>
+#include <QGroupBox>
+#include <QLineEdit>
+#include <QObject>
+#include <QSpinBox>
+#include <QTextEdit>
+
+#include "colorbutton.h"
+#include "fontselector.h"
+#include "funchelpers.h"
+#include "util.h"
+
+namespace detail {
+
+/**
+ * Contains all supported widget changed signals.
+ */
+static const auto supportedWidgetChangedSignals = std::make_tuple(
+            &ColorButton::colorChanged,
+            &FontSelector::fontChanged,
+            &QAbstractButton::toggled,
+            selectOverload<int>(&QComboBox::currentIndexChanged),
+            selectOverload<double>(&QDoubleSpinBox::valueChanged),
+            &QGroupBox::toggled,
+            &QLineEdit::textChanged,
+            selectOverload<int>(&QSpinBox::valueChanged),
+            &QTextEdit::textChanged
+);
+
+/**
+ * Tries to find a changed signal that matches the given widget type, and connects that to the given receiver/slot.
+ */
+template<typename Receiver, typename Slot, std::size_t ...Is>
+bool tryConnectChangedSignal(const QObject* widget, const Receiver* receiver, Slot slot, std::index_sequence<Is...>)
+{
+    // Tries to cast the given QObject to the given signal's class type, and connects to receiver/slot if successful.
+    // If *alreadyConnected is true, just returns false to prevent multiple connections.
+    static const auto tryConnect = [](const QObject* object, auto sig, auto receiver, auto slot, bool* alreadyConnected) {
+        if (!*alreadyConnected) {
+            auto widget = qobject_cast<const typename MemberFunction<decltype(sig)>::ClassType *>(object);
+            if (widget) {
+                *alreadyConnected = QObject::connect(widget, sig, receiver, slot);
+                return *alreadyConnected;
+            }
+        }
+        return false;
+    };
+
+    // Unpack the tuple and try to connect to each contained signal in order
+    bool alreadyConnected{false};
+    auto results = { tryConnect(widget, std::get<Is>(supportedWidgetChangedSignals), receiver, slot, &alreadyConnected)... };
+    return std::any_of(results.begin(), results.end(), [](bool result) { return result; });
+}
+
+}  //detail
+
+/**
+ * Connects the given widget's changed signal to the given receiver/context and slot.
+ *
+ * Changed signals for supported widgets are listed in detail::supportedWidgetChangedSignals.
+ *
+ * @note The slot must not take any arguments.
+ *
+ * @param widget   Pointer to the widget
+ * @param receiver Receiver of the signal (or context for the connection)
+ * @param slot     Receiving slot (or functor, or whatever)
+ * @returns true if the widget type is supported, false otherwise
+ */
+template<typename Receiver, typename Slot>
+bool connectToWidgetChangedSignal(const QObject* widget, const Receiver* receiver, Slot slot)
+{
+    return detail::tryConnectChangedSignal(widget, receiver, slot,
+                                           std::make_index_sequence<std::tuple_size<decltype(detail::supportedWidgetChangedSignals)>::value>{});
+}
+
+/**
+ * Connects the given widgets' changed signals to the given receiver/context and slot.
+ *
+ * Changed signals for supported widgets are listed in detail::supportedWidgetChangedSignals.
+ *
+ * @note The slot must not take any arguments.
+ *
+ * @param widget   Pointer to the widget
+ * @param receiver Receiver of the signal (or context for the connection)
+ * @param slot     Receiving slot (or functor, or whatever)
+ * @returns true if all given widget types are supported, false otherwise
+ */
+template<typename Container, typename Receiver, typename Slot>
+bool connectToWidgetsChangedSignals(const Container& widgets, const Receiver* receiver, Slot slot)
+{
+    bool success = true;
+    for (auto&& widget : widgets) {
+        success &= detail::tryConnectChangedSignal(widget, receiver, slot,
+                                                   std::make_index_sequence<std::tuple_size<decltype(detail::supportedWidgetChangedSignals)>::value>{});
+    }
+    return success;
+}
+
+/**
+ * @overload
+ * Convenience overload that allows brace-initializing the list of widgets.
+ */
+template<typename Receiver, typename Slot>
+bool connectToWidgetsChangedSignals(const std::initializer_list<const QObject*>& widgets, const Receiver* receiver, Slot slot)
+{
+    return connectToWidgetsChangedSignals(std::vector<const QObject*>{widgets}, receiver, slot);
+}