소스 검색

Merge pull request #1664 from Xaymar/feature-property-groups

libobs/UI: Add Property Grouping
Jim 6 년 전
부모
커밋
c27a8e7ae9
4개의 변경된 파일204개의 추가작업 그리고 6개의 파일을 삭제
  1. 51 1
      UI/properties-view.cpp
  2. 3 0
      UI/properties-view.hpp
  3. 133 5
      libobs/obs-properties.c
  4. 17 0
      libobs/obs-properties.h

+ 51 - 1
UI/properties-view.cpp

@@ -19,6 +19,7 @@
 #include <QMenu>
 #include <QStackedWidget>
 #include <QDir>
+#include <QGroupBox>
 #include "double-slider.hpp"
 #include "slider-ignorewheel.hpp"
 #include "spinBox-ignorewheel.hpp"
@@ -1332,6 +1333,44 @@ void OBSPropertiesView::AddFrameRate(obs_property_t *prop, bool &warning,
 	});
 }
 
+void OBSPropertiesView::AddGroup(obs_property_t *prop, QFormLayout *layout)
+{
+	const char *name = obs_property_name(prop);
+	bool val = obs_data_get_bool(settings, name);
+	const char *desc = obs_property_description(prop);
+	enum obs_group_type type = obs_property_group_type(prop);
+
+	// Create GroupBox
+	QGroupBox *groupBox = new QGroupBox(QT_UTF8(desc));
+	groupBox->setCheckable(type == OBS_GROUP_CHECKABLE);
+	groupBox->setChecked(groupBox->isCheckable() ? val : true);
+	groupBox->setAccessibleName("group");
+	groupBox->setEnabled(obs_property_enabled(prop));
+
+	// Create Layout and build content
+	QFormLayout *subLayout = new QFormLayout();
+	subLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
+	groupBox->setLayout(subLayout);
+
+	obs_properties_t *content = obs_property_group_content(prop);
+	obs_property_t *el = obs_properties_first(content);
+	while (el != nullptr) {
+		AddProperty(el, subLayout);
+		obs_property_next(&el);
+	}
+
+	// Insert into UI
+	layout->setWidget(layout->rowCount(),
+			QFormLayout::ItemRole::SpanningRole, groupBox);
+
+	// Register Group Widget
+	WidgetInfo *info = new WidgetInfo(this, prop, groupBox);
+	children.emplace_back(info);
+
+	// Signals
+	connect(groupBox, SIGNAL(toggled()), info, SLOT(ControlChanged()));
+}
+
 void OBSPropertiesView::AddProperty(obs_property_t *property,
 		QFormLayout *layout)
 {
@@ -1381,6 +1420,8 @@ void OBSPropertiesView::AddProperty(obs_property_t *property,
 	case OBS_PROPERTY_FRAME_RATE:
 		AddFrameRate(property, warning, layout, label);
 		break;
+	case OBS_PROPERTY_GROUP:
+		AddGroup(property, layout);
 	}
 
 	if (widget && !obs_property_enabled(property))
@@ -1388,7 +1429,8 @@ void OBSPropertiesView::AddProperty(obs_property_t *property,
 
 	if (!label &&
 	    type != OBS_PROPERTY_BOOL &&
-	    type != OBS_PROPERTY_BUTTON)
+	    type != OBS_PROPERTY_BUTTON &&
+	    type != OBS_PROPERTY_GROUP)
 		label = new QLabel(QT_UTF8(obs_property_description(property)));
 
 	if (warning && label) //TODO: select color based on background color
@@ -1707,6 +1749,13 @@ bool WidgetInfo::FontChanged(const char *setting)
 	return true;
 }
 
+void WidgetInfo::GroupChanged(const char *setting)
+{
+	QGroupBox *groupbox = static_cast<QGroupBox*>(widget);
+	obs_data_set_bool(view->settings, setting,
+		groupbox->isCheckable() ? groupbox->isChecked() : true);
+}
+
 void WidgetInfo::EditableListChanged()
 {
 	const char *setting = obs_property_name(property);
@@ -1776,6 +1825,7 @@ void WidgetInfo::ControlChanged()
 		if (!FrameRateChanged(widget, setting, view->settings))
 			return;
 		break;
+	case OBS_PROPERTY_GROUP:  GroupChanged(setting); return;
 	}
 
 	if (view->callback && !view->deferUpdate)

+ 3 - 0
UI/properties-view.hpp

@@ -33,6 +33,7 @@ private:
 	void ListChanged(const char *setting);
 	bool ColorChanged(const char *setting);
 	bool FontChanged(const char *setting);
+	void GroupChanged(const char *setting);
 	void EditableListChanged();
 	void ButtonClicked();
 
@@ -103,6 +104,8 @@ private:
 	void AddFrameRate(obs_property_t *prop, bool &warning,
 			QFormLayout *layout, QLabel *&label);
 
+	void AddGroup(obs_property_t *prop, QFormLayout *layout);
+
 	void AddProperty(obs_property_t *property, QFormLayout *layout);
 
 	void resizeEvent(QResizeEvent *event) override;

+ 133 - 5
libobs/obs-properties.c

@@ -86,6 +86,11 @@ struct frame_rate_data {
 	DARRAY(struct frame_rate_range)  ranges;
 };
 
+struct group_data {
+	enum obs_group_type type;
+	obs_properties_t *content;
+};
+
 static inline void path_data_free(struct path_data *data)
 {
 	bfree(data->default_path);
@@ -140,6 +145,10 @@ static inline void frame_rate_data_free(struct frame_rate_data *data)
 	da_free(data->ranges);
 }
 
+static inline void group_data_free(struct group_data *data) {
+	obs_properties_destroy(data->content);
+}
+
 struct obs_properties;
 
 struct obs_property {
@@ -166,6 +175,7 @@ struct obs_properties {
 
 	struct obs_property     *first_property;
 	struct obs_property     **last;
+	struct obs_property     *parent;
 };
 
 obs_properties_t *obs_properties_create(void)
@@ -223,6 +233,8 @@ static void obs_property_destroy(struct obs_property *property)
 		editable_list_data_free(get_property_data(property));
 	else if (property->type == OBS_PROPERTY_FRAME_RATE)
 		frame_rate_data_free(get_property_data(property));
+	else if (property->type == OBS_PROPERTY_GROUP)
+		group_data_free(get_property_data(property));
 
 	bfree(property->name);
 	bfree(property->desc);
@@ -265,12 +277,26 @@ obs_property_t *obs_properties_get(obs_properties_t *props, const char *name)
 		if (strcmp(property->name, name) == 0)
 			return property;
 
+		if (property->type == OBS_PROPERTY_GROUP) {
+			obs_properties_t *group =
+				obs_property_group_content(property);
+			obs_property_t *found = obs_properties_get(group, name);
+			if (found != NULL) {
+				return found;
+			}
+		}
+
 		property = property->next;
 	}
 
 	return NULL;
 }
 
+obs_properties_t *obs_properties_get_parent(obs_properties_t *props)
+{
+	return props->parent ? props->parent->parent : NULL;
+}
+
 void obs_properties_remove_by_name(obs_properties_t *props, const char *name)
 {
 	if (!props)
@@ -338,6 +364,7 @@ static inline size_t get_property_size(enum obs_property_type type)
 	case OBS_PROPERTY_EDITABLE_LIST:
 		return sizeof(struct editable_list_data);
 	case OBS_PROPERTY_FRAME_RATE:return sizeof(struct frame_rate_data);
+	case OBS_PROPERTY_GROUP:     return sizeof(struct group_data);
 	}
 
 	return 0;
@@ -362,7 +389,18 @@ static inline struct obs_property *new_prop(struct obs_properties *props,
 	return p;
 }
 
-static inline bool has_prop(struct obs_properties *props, const char *name)
+static inline obs_properties_t *get_topmost_parent(obs_properties_t *props)
+{
+	obs_properties_t *parent = props;
+	obs_properties_t *last_parent = parent;
+	while (parent) {
+		last_parent = parent;
+		parent = obs_properties_get_parent(parent);
+	}
+	return last_parent;
+}
+
+static inline bool contains_prop(struct obs_properties *props, const char *name)
 {
 	struct obs_property *p = props->first_property;
 
@@ -372,12 +410,23 @@ static inline bool has_prop(struct obs_properties *props, const char *name)
 			return true;
 		}
 
+		if (p->type == OBS_PROPERTY_GROUP) {
+			if (contains_prop(obs_property_group_content(p), name)) {
+				return true;
+			}
+		}
+
 		p = p->next;
 	}
 
 	return false;
 }
 
+static inline bool has_prop(struct obs_properties *props, const char *name)
+{
+	return contains_prop(get_topmost_parent(props), name);
+}
+
 static inline void *get_property_data(struct obs_property *prop)
 {
 	return (uint8_t*)prop + sizeof(struct obs_property);
@@ -578,6 +627,70 @@ obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props,
 	return p;
 }
 
+static bool check_property_group_recursion(obs_properties_t *parent,
+	obs_properties_t *group)
+{
+	/* Scan the group for the parent. */
+	obs_property_t *current_property = group->first_property;
+	while (current_property) {
+		if (current_property->type == OBS_PROPERTY_GROUP) {
+			obs_properties_t *cprops =
+				obs_property_group_content(current_property);
+			if (cprops == parent) {
+				/* Contains find_props */
+				return true;
+			} else if (cprops == group) {
+				/* Contains self, shouldn't be possible but
+				 * lets verify anyway. */
+				return true;
+			}
+			check_property_group_recursion(cprops, group);
+		}
+
+		current_property = current_property->next;
+	}
+
+	return false;
+}
+
+static bool check_property_group_duplicates(obs_properties_t *parent,
+	obs_properties_t *group)
+{
+	obs_property_t *current_property = group->first_property;
+	while (current_property) {
+		if (has_prop(parent, current_property->name)) {
+			return true;
+		}
+
+		current_property = current_property->next;
+	}
+
+	return false;
+}
+
+obs_property_t *obs_properties_add_group(obs_properties_t *props,
+	const char *name, const char *desc, enum obs_group_type type,
+	obs_properties_t *group)
+{
+	if (!props || has_prop(props, name)) return NULL;
+	if (!group) return NULL;
+
+	/* Prevent recursion. */
+	if (props == group) return NULL;
+	if (check_property_group_recursion(props, group)) return NULL;
+
+	/* Prevent duplicate properties */
+	if (check_property_group_duplicates(props, group)) return NULL;
+
+	obs_property_t *p = new_prop(props, name, desc, OBS_PROPERTY_GROUP);
+	group->parent = p;
+
+	struct group_data *data = get_property_data(p);
+	data->type = type;
+	data->content = group;
+	return p;
+}
+
 /* ------------------------------------------------------------------------- */
 
 static inline bool is_combo(struct obs_property *p)
@@ -630,9 +743,11 @@ bool obs_property_modified(obs_property_t *p, obs_data_t *settings)
 {
 	if (p) {
 		if (p->modified) {
-			return p->modified(p->parent, p, settings);
+			obs_properties_t *top = get_topmost_parent(p->parent);
+			return p->modified(top, p, settings);
 		} else if (p->modified2) {
-			return p->modified2(p->priv, p->parent, p, settings);
+			obs_properties_t *top = get_topmost_parent(p->parent);
+			return p->modified2(p->priv, top, p, settings);
 		}
 	}
 	return false;
@@ -645,9 +760,10 @@ bool obs_property_button_clicked(obs_property_t *p, void *obj)
 		struct button_data *data = get_type_data(p,
 				OBS_PROPERTY_BUTTON);
 		if (data && data->callback) {
+			obs_properties_t *top = get_topmost_parent(p->parent);
 			if (p->priv)
-				return data->callback(p->parent, p, p->priv);
-			return data->callback(p->parent, p,
+				return data->callback(top, p, p->priv);
+			return data->callback(top, p,
 					(context ? context->data : NULL));
 		}
 	}
@@ -1137,3 +1253,15 @@ enum obs_text_type obs_proprety_text_type(obs_property_t *p)
 {
 	return obs_property_text_type(p);
 }
+
+enum obs_group_type obs_property_group_type(obs_property_t *p)
+{
+	struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);
+	return data ? data->type : OBS_COMBO_INVALID;
+}
+
+obs_properties_t *obs_property_group_content(obs_property_t *p)
+{
+	struct group_data *data = get_type_data(p, OBS_PROPERTY_GROUP);
+	return data ? data->content : NULL;
+}

+ 17 - 0
libobs/obs-properties.h

@@ -55,6 +55,7 @@ enum obs_property_type {
 	OBS_PROPERTY_FONT,
 	OBS_PROPERTY_EDITABLE_LIST,
 	OBS_PROPERTY_FRAME_RATE,
+	OBS_PROPERTY_GROUP,
 };
 
 enum obs_combo_format {
@@ -93,6 +94,12 @@ enum obs_number_type {
 	OBS_NUMBER_SLIDER
 };
 
+enum obs_group_type {
+	OBS_COMBO_INVALID,
+	OBS_GROUP_NORMAL,
+	OBS_GROUP_CHECKABLE,
+};
+
 #define OBS_FONT_BOLD      (1<<0)
 #define OBS_FONT_ITALIC    (1<<1)
 #define OBS_FONT_UNDERLINE (1<<2)
@@ -122,6 +129,8 @@ EXPORT obs_property_t *obs_properties_first(obs_properties_t *props);
 EXPORT obs_property_t *obs_properties_get(obs_properties_t *props,
 		const char *property);
 
+EXPORT obs_properties_t *obs_properties_get_parent(obs_properties_t *props);
+
 /** Remove a property from a properties list.
  *
  * Removes a property from a properties list. Only valid in either
@@ -231,6 +240,11 @@ EXPORT obs_property_t *obs_properties_add_editable_list(obs_properties_t *props,
 EXPORT obs_property_t *obs_properties_add_frame_rate(obs_properties_t *props,
 		const char *name, const char *description);
 
+EXPORT obs_property_t *obs_properties_add_group(obs_properties_t *props,
+	const char *name, const char *description, enum obs_group_type type,
+	obs_properties_t *group);
+
+
 /* ------------------------------------------------------------------------- */
 
 /**
@@ -349,6 +363,9 @@ EXPORT struct media_frames_per_second obs_property_frame_rate_fps_range_min(
 EXPORT struct media_frames_per_second obs_property_frame_rate_fps_range_max(
 		obs_property_t *p, size_t idx);
 
+EXPORT enum obs_group_type obs_property_group_type(obs_property_t *p);
+EXPORT obs_properties_t *obs_property_group_content(obs_property_t *p);
+
 #ifndef SWIG
 DEPRECATED
 EXPORT enum obs_text_type     obs_proprety_text_type(obs_property_t *p);