From d15fe49c71e1bfe89a0d28c420906dac6a12313f Mon Sep 17 00:00:00 2001 From: vhaudiquet Date: Thu, 20 Jul 2023 22:34:35 +0200 Subject: [PATCH] Initial commit Added basic UI --- .clang-format | 55 ++++++++ .gitignore | 1 + Makefile | 46 ++++++ src/main.c | 7 + src/ui/ginput-application.c | 87 ++++++++++++ src/ui/ginput-application.h | 11 ++ src/ui/main-window.c | 275 ++++++++++++++++++++++++++++++++++++ src/ui/main-window.h | 11 ++ src/ui/panel.c | 206 +++++++++++++++++++++++++++ src/ui/panel.h | 44 ++++++ src/ui/panels/empty_panel.c | 30 ++++ src/ui/panels/empty_panel.h | 11 ++ ui/ginput.cmb | 74 ++++++++++ ui/ginput.gresource.xml | 9 ++ ui/style.css | 1 + v.ginput.gschema.xml | 10 ++ 16 files changed, 878 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 src/main.c create mode 100644 src/ui/ginput-application.c create mode 100644 src/ui/ginput-application.h create mode 100644 src/ui/main-window.c create mode 100644 src/ui/main-window.h create mode 100644 src/ui/panel.c create mode 100644 src/ui/panel.h create mode 100644 src/ui/panels/empty_panel.c create mode 100644 src/ui/panels/empty_panel.h create mode 100644 ui/ginput.cmb create mode 100644 ui/ginput.gresource.xml create mode 100644 ui/style.css create mode 100644 v.ginput.gschema.xml diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..3c172c4 --- /dev/null +++ b/.clang-format @@ -0,0 +1,55 @@ +--- +Language: Cpp +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: true + AfterControlStatement: Always + AfterEnum: true + AfterFunction: true + AfterStruct: true + AfterUnion: true + BeforeElse: true + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: false +# BreakBeforeBraces: Allman +BreakStringLiterals: false +ColumnLimit: 0 +ContinuationIndentWidth: 2 +IndentCaseBlocks: false +IndentCaseLabels: true +PointerAlignment: Left + +# Indent +IndentWidth: 4 +TabWidth: 4 +UseTab: ForIndentation + +# Spaces +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeParens: Custom +SpaceBeforeParensOptions: + AfterControlStatements: false + AfterFunctionDeclarationName: false + AfterFunctionDefinitionName: false + BeforeNonEmptyParentheses: false +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false + +# Preprocessor +SortIncludes: Never diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..567609b --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..76ea2a2 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +NAME=ginput +CC=gcc +CFLAGS=$(shell pkg-config --cflags gtk4) $(shell pkg-config --cflags libadwaita-1) -O3 -I src +LDFLAGS=$(shell pkg-config --libs gtk4) $(shell pkg-config --libs libadwaita-1) +BUILD_DIR=build + +CMB := $(wildcard ui/*.cmb) +C_FILES := $(shell find $(src) -name '*.c') + +all: resources $(BUILD_DIR)/$(NAME) + +# Top-level targets +resources: $(BUILD_DIR)/glib-2.0/schemas/gschemas.compiled + +$(BUILD_DIR)/$(NAME): $(BUILD_DIR)/resource.c $(C_FILES) | $(BUILD_DIR) + gcc $(CFLAGS) -o $@ $^ $(LDFLAGS) + +# Build directory +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# UI : Cambalache contains all of the UI files, just compile them all +$(BUILD_DIR)/%.ui: ui/$(NAME).cmb | $(BUILD_DIR) + cambalache -E $^ + mv ui/*.ui $(BUILD_DIR)/ + +# Resources +$(BUILD_DIR)/resource.c: ui/ginput.gresource.xml ui/style.css $(BUILD_DIR)/main-window.ui + cp ui/style.css $(BUILD_DIR)/ + cp ui/ginput.gresource.xml $(BUILD_DIR)/ + cd $(BUILD_DIR) && glib-compile-resources --generate-source --target=resource.c ginput.gresource.xml + +$(BUILD_DIR)/glib-2.0/schemas/gschemas.compiled: v.ginput.gschema.xml | $(BUILD_DIR) + mkdir -p $(BUILD_DIR)/glib-2.0/schemas + glib-compile-schemas . --targetdir=$(BUILD_DIR)/glib-2.0/schemas + +# Phony targets +.PHONY: clean +clean: + rm -f ui/*.ui # Remove compiled UI files that might be left over + rm -rf $(BUILD_DIR) + +.PHONY: run +.SILENT: run +run: all + XDG_DATA_DIRS=./$(BUILD_DIR) ./$(BUILD_DIR)/ginput \ No newline at end of file diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5d4edc0 --- /dev/null +++ b/src/main.c @@ -0,0 +1,7 @@ +#include "ui/ginput-application.h" + +int main(gint argc, gchar** argv) +{ + AdwApplication* application = ginput_application_new(); + return g_application_run(G_APPLICATION(application), argc, argv); +} diff --git a/src/ui/ginput-application.c b/src/ui/ginput-application.c new file mode 100644 index 0000000..d37eba1 --- /dev/null +++ b/src/ui/ginput-application.c @@ -0,0 +1,87 @@ +#include "ginput-application.h" +#include "ui/main-window.h" + +struct _GinputApplication +{ + AdwApplication parent; + + MainWindow* window; +}; + +G_DEFINE_TYPE(GinputApplication, ginput_application, ADW_TYPE_APPLICATION) + +/* + * Special 'activate' trigger of the application, called when the application is launched + * This shows the main window + */ +static void +ginput_application_activate(GApplication* application) +{ + GinputApplication* self = GINPUT_APPLICATION(application); + gtk_window_present(GTK_WINDOW(self->window)); +} + +static void +ginput_application_startup(GApplication* application) +{ + GinputApplication* self = GINPUT_APPLICATION(application); + + G_APPLICATION_CLASS(ginput_application_parent_class)->startup(application); + + self->window = main_window_new(ADW_APPLICATION(application)); + + GtkCssProvider* provider = gtk_css_provider_new(); + gtk_css_provider_load_from_resource(provider, "/v/ginput/style.css"); + gtk_style_context_add_provider_for_display(gdk_display_get_default(), + GTK_STYLE_PROVIDER(provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); +} + +static void +ginput_application_finalize(GObject* object) +{ + G_OBJECT_CLASS(ginput_application_parent_class)->finalize(object); +} + +static GObject* +ginput_application_constructor(GType type, guint n_construct_params, GObjectConstructParam* construct_params) +{ + static GObject* self = NULL; + + if(self == NULL) + { + self = G_OBJECT_CLASS(ginput_application_parent_class)->constructor(type, n_construct_params, construct_params); + g_object_add_weak_pointer(self, (gpointer) &self); + return self; + } + + return g_object_ref(self); +} + +static void +ginput_application_class_init(GinputApplicationClass* klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + GApplicationClass* application_class = G_APPLICATION_CLASS(klass); + + object_class->finalize = ginput_application_finalize; + object_class->constructor = ginput_application_constructor; + application_class->activate = ginput_application_activate; + application_class->startup = ginput_application_startup; + // application_class->command_line = ginput_application_command_line; + // application_class->handle_local_options = ginput_application_handle_local_options; +} + +static void +ginput_application_init(GinputApplication* self) +{ +} + +AdwApplication* +ginput_application_new(void) +{ + return g_object_new(ginput_application_get_type(), + "application-id", "v.ginput", + "flags", G_APPLICATION_DEFAULT_FLAGS, + NULL); +} diff --git a/src/ui/ginput-application.h b/src/ui/ginput-application.h new file mode 100644 index 0000000..cd3f98a --- /dev/null +++ b/src/ui/ginput-application.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(GinputApplication, ginput_application, GINPUT, APPLICATION, AdwApplication) + +AdwApplication* ginput_application_new(void); + +G_END_DECLS diff --git a/src/ui/main-window.c b/src/ui/main-window.c new file mode 100644 index 0000000..be9c546 --- /dev/null +++ b/src/ui/main-window.c @@ -0,0 +1,275 @@ +#define G_LOG_DOMAIN "main-window" + +#include "main-window.h" +#include "panels/empty_panel.h" + +#include + +struct _MainWindow +{ + AdwApplicationWindow parent; + + AdwHeaderBar* header; + AdwLeaflet* main_leaflet; + // TODO LIST : CcPanelList *panel_list; + GtkBox* sidebar_box; + AdwWindowTitle* sidebar_title_widget; + GtkStack* stack; + + // GtkWidget *old_panel; + GtkWidget* current_panel; + char* current_panel_id; + // GQueue *previous_panels; + + GtkWidget* custom_titlebar; + + // CcShellModel *store; + + // CcPanel *active_panel; + GSettings* settings; + + gboolean folded; + + // CcPanelListView previous_list_view; +}; + +G_DEFINE_TYPE(MainWindow, main_window, ADW_TYPE_APPLICATION_WINDOW) +enum +{ + PROP_0, + PROP_ACTIVE_PANEL, + PROP_MODEL, + PROP_FOLDED, +}; + +static gboolean activate_panel(MainWindow* self, Panel* panel, const gchar* id) +{ + self->current_panel = GTK_WIDGET(panel); + gtk_stack_add_named(self->stack, self->current_panel, id); + gtk_stack_set_visible_child_name (self->stack, id); +} + +/* + * @Override from GtkWidget + */ +static void +main_window_map(GtkWidget* widget) +{ + MainWindow* self = (MainWindow*) widget; + GTK_WIDGET_CLASS(main_window_parent_class)->map(widget); +} + +/* + * @Override from GtkWidget + */ +static void +main_window_unmap(GtkWidget* widget) +{ + MainWindow* self = GINPUT_MAIN_WINDOW(widget); + gboolean maximized; + gint height; + gint width; + + maximized = gtk_window_is_maximized(GTK_WINDOW(self)); + gtk_window_get_default_size(GTK_WINDOW(self), &width, &height); + + g_settings_set(self->settings, + "window-state", + "(iib)", + width, + height, + maximized); + + GTK_WIDGET_CLASS(main_window_parent_class)->unmap(widget); +} + +static void +main_window_get_property(GObject* object, guint property_id, GValue* value, GParamSpec* pspec) +{ + MainWindow* self = GINPUT_MAIN_WINDOW(object); + + switch(property_id) + { + // case PROP_ACTIVE_PANEL: + // g_value_set_object (value, self->active_panel); + // break; + + // case PROP_MODEL: + // g_value_set_object (value, self->store); + // break; + + case PROP_FOLDED: + g_value_set_boolean(value, self->folded); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +main_window_set_property(GObject* object, + guint property_id, + const GValue* value, + GParamSpec* pspec) +{ + MainWindow* self = GINPUT_MAIN_WINDOW(object); + + switch(property_id) + { + // case PROP_ACTIVE_PANEL: + // set_active_panel(self, g_value_get_object(value)); + // break; + + // case PROP_MODEL: + // g_assert(self->store == NULL); + // self->store = g_value_dup_object(value); + // break; + + case PROP_FOLDED: + self->folded = g_value_get_boolean(value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec); + } +} + +static void +load_window_state(MainWindow* self) +{ + gint current_width = -1; + gint current_height = -1; + gboolean maximized = FALSE; + + g_settings_get(self->settings, + "window-state", + "(iib)", + ¤t_width, + ¤t_height, + &maximized); + + if(current_width != -1 && current_height != -1) + gtk_window_set_default_size(GTK_WINDOW(self), current_width, current_height); + if(maximized) + gtk_window_maximize(GTK_WINDOW(self)); +} + +static void +main_window_constructed(GObject* object) +{ + MainWindow* self = GINPUT_MAIN_WINDOW(object); + + load_window_state(self); + + /* Add the panels */ + // setup_model (self); + + /* After everything is loaded, select the last used panel, if any, + * or the first visible panel. We do that in an idle handler so we + * have a chance to skip it when another panel has been explicitly + * activated from commandline parameter or from DBus method */ + // g_idle_add_once ((GSourceOnceFunc) maybe_load_last_panel, self); + + // g_signal_connect_swapped (self->panel_list, + // "notify::view", + // G_CALLBACK (update_headerbar_buttons), + // self); + + // update_headerbar_buttons (self); + // adw_leaflet_set_visible_child (self->main_leaflet, + // GTK_WIDGET (self->sidebar_box)); + + G_OBJECT_CLASS(main_window_parent_class)->constructed(object); +} + +static void +main_window_dispose(GObject* object) +{ + MainWindow* self = GINPUT_MAIN_WINDOW(object); + + g_clear_pointer(&self->current_panel_id, g_free); + // g_clear_object (&self->store); + // g_clear_object (&self->active_panel); + + G_OBJECT_CLASS(main_window_parent_class)->dispose(object); +} + +static void +main_window_finalize(GObject* object) +{ + MainWindow* self = GINPUT_MAIN_WINDOW(object); + + // if (self->previous_panels) + // { + // g_queue_free_full (self->previous_panels, g_free); + // self->previous_panels = NULL; + // } + + g_clear_object(&self->settings); + + G_OBJECT_CLASS(main_window_parent_class)->finalize(object); +} + +static void +main_window_class_init(MainWindowClass* klass) +{ + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + GObjectClass* object_class = G_OBJECT_CLASS(klass); + + object_class->get_property = main_window_get_property; + object_class->set_property = main_window_set_property; + object_class->constructed = main_window_constructed; + object_class->dispose = main_window_dispose; + object_class->finalize = main_window_finalize; + + widget_class->map = main_window_map; + widget_class->unmap = main_window_unmap; + + g_object_class_install_property(object_class, PROP_FOLDED, + g_param_spec_boolean("folded", "Folded", "Whether the window is foled", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gtk_widget_class_set_template_from_resource(widget_class, "/v/ginput/main-window.ui"); + + gtk_widget_class_bind_template_child(widget_class, MainWindow, header); + gtk_widget_class_bind_template_child(widget_class, MainWindow, main_leaflet); + // gtk_widget_class_bind_template_child(widget_class, MainWindow, panel_list); + gtk_widget_class_bind_template_child(widget_class, MainWindow, sidebar_box); + gtk_widget_class_bind_template_child(widget_class, MainWindow, sidebar_title_widget); + gtk_widget_class_bind_template_child(widget_class, MainWindow, stack); +} + +static void +main_window_init(MainWindow* self) +{ + gtk_widget_init_template(GTK_WIDGET(self)); + + self->settings = g_settings_new("v.ginput"); + // self->previous_panels = g_queue_new (); + // self->previous_list_view = cc_panel_list_get_view (self->panel_list); + + g_object_bind_property(self->main_leaflet, + "folded", + self, + "folded", + G_BINDING_SYNC_CREATE); + + activate_panel(self, empty_panel_new(), "empty"); +} + +// TODO use translation +#define _(x) x + +MainWindow* +main_window_new(AdwApplication* application) +{ + g_return_val_if_fail(GTK_IS_APPLICATION(application), NULL); + + return g_object_new(main_window_get_type(), + "application", application, + "resizable", TRUE, + "title", _("Input devices"), + "icon-name", "input-devices", + "show-menubar", FALSE, + NULL); +} diff --git a/src/ui/main-window.h b/src/ui/main-window.h new file mode 100644 index 0000000..7792408 --- /dev/null +++ b/src/ui/main-window.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(MainWindow, main_window, GINPUT, MAIN_WINDOW, AdwApplicationWindow) + +MainWindow* main_window_new(AdwApplication* application); + +G_END_DECLS diff --git a/src/ui/panel.c b/src/ui/panel.c new file mode 100644 index 0000000..a72eb5d --- /dev/null +++ b/src/ui/panel.c @@ -0,0 +1,206 @@ +#include "panel.h" + +/* + * Abstract class representing a panel + */ + +typedef struct +{ + AdwBin* content_bin; + GtkBox* main_box; + AdwBin* titlebar_bin; + AdwHeaderBar* titlebar; + + GCancellable* cancellable; + gboolean folded; + gchar* title; +} PanelPrivate; + +static void panel_buildable_init(GtkBuildableIface* iface); + +G_DEFINE_ABSTRACT_TYPE_WITH_CODE(Panel, panel, ADW_TYPE_BIN, + G_ADD_PRIVATE(Panel) + G_IMPLEMENT_INTERFACE(GTK_TYPE_BUILDABLE, panel_buildable_init)) + +static GtkBuildableIface* parent_buildable_iface; + +enum +{ + PROP_0, + PROP_PARAMETERS, + PROP_FOLDED, + PROP_TITLE, + N_PROPS +}; + +static GParamSpec* properties[N_PROPS]; + +static void +panel_buildable_add_child(GtkBuildable* buildable, GtkBuilder* builder, GObject* child, const char* type) +{ + PanelPrivate* priv = panel_get_instance_private(GINPUT_PANEL(buildable)); + + if(GTK_IS_WIDGET(child) && !priv->main_box) + { + adw_bin_set_child(ADW_BIN(buildable), GTK_WIDGET(child)); + return; + } + + if(g_strcmp0(type, "content") == 0) + adw_bin_set_child(priv->content_bin, GTK_WIDGET(child)); + else if(g_strcmp0(type, "titlebar-start") == 0) + adw_header_bar_pack_start(priv->titlebar, GTK_WIDGET(child)); + else if(g_strcmp0(type, "titlebar-end") == 0) + adw_header_bar_pack_end(priv->titlebar, GTK_WIDGET(child)); + else if(g_strcmp0(type, "titlebar") == 0) + adw_bin_set_child(priv->titlebar_bin, GTK_WIDGET(child)); + else + parent_buildable_iface->add_child(buildable, builder, child, type); +} + +/* + * @Override from GtkBuildableIface + */ +static void +panel_buildable_init(GtkBuildableIface* iface) +{ + parent_buildable_iface = g_type_interface_peek_parent(iface); + iface->add_child = panel_buildable_add_child; +} + +/* + * @Override from GObject + */ +static void +panel_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec) +{ + PanelPrivate* priv = panel_get_instance_private(GINPUT_PANEL(object)); + + switch(prop_id) + { + case PROP_PARAMETERS: + { + g_autoptr(GVariant) v = NULL; + GVariant* parameters; + gsize n_parameters; + + parameters = g_value_get_variant(value); + + if(parameters == NULL) + return; + + n_parameters = g_variant_n_children(parameters); + if(n_parameters == 0) + return; + + g_variant_get_child(parameters, 0, "v", &v); + + if(!g_variant_is_of_type(v, G_VARIANT_TYPE_DICTIONARY)) + g_warning("Wrong type for the first argument GVariant, expected 'a{sv}' but got '%s'", + (gchar*) g_variant_get_type(v)); + else if(g_variant_n_children(v) > 0) + g_warning("Ignoring additional flags"); + + if(n_parameters > 1) + g_warning("Ignoring additional parameters"); + + break; + } + + case PROP_TITLE: + priv->title = g_value_dup_string(value); + break; + + case PROP_FOLDED: + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/* + * @Override from GObject + */ +static void +panel_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec) +{ + PanelPrivate* priv = panel_get_instance_private(GINPUT_PANEL(object)); + + switch(prop_id) + { + case PROP_FOLDED: + g_value_set_boolean(value, priv->folded); + break; + + case PROP_TITLE: + g_value_set_string(value, priv->title); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +/* + * @Override from GObject + */ +static void +panel_finalize(GObject* object) +{ + PanelPrivate* priv = panel_get_instance_private(GINPUT_PANEL(object)); + + g_cancellable_cancel(priv->cancellable); + g_clear_object(&priv->cancellable); + + g_clear_pointer(&priv->title, g_free); + + G_OBJECT_CLASS(panel_parent_class)->finalize(object); +} + +static void +panel_class_init(PanelClass* klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + + object_class->get_property = panel_get_property; + object_class->set_property = panel_set_property; + object_class->finalize = panel_finalize; + + // signals[SIDEBAR_ACTIVATED] = g_signal_new ("sidebar-activated", + // G_TYPE_FROM_CLASS (object_class), + // G_SIGNAL_RUN_LAST, + // 0, NULL, NULL, + // g_cclosure_marshal_VOID__VOID, + // G_TYPE_NONE, 0); + + properties[PROP_FOLDED] = g_param_spec_boolean("folded", NULL, NULL, + FALSE, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_PARAMETERS] = g_param_spec_variant("parameters", + "Structured parameters", + "Additional parameters passed externally (ie. command line, D-Bus activation)", + G_VARIANT_TYPE("av"), + NULL, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS); + + properties[PROP_TITLE] = g_param_spec_string("title", NULL, NULL, NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object_class, N_PROPS, properties); + + gtk_widget_class_set_template_from_resource(widget_class, "/v/ginput/panel.ui"); + + gtk_widget_class_bind_template_child_private(widget_class, Panel, content_bin); + gtk_widget_class_bind_template_child_private(widget_class, Panel, main_box); + gtk_widget_class_bind_template_child_private(widget_class, Panel, titlebar_bin); + gtk_widget_class_bind_template_child_private(widget_class, Panel, titlebar); +} + +static void +panel_init(Panel* panel) +{ + gtk_widget_init_template(GTK_WIDGET(panel)); +} diff --git a/src/ui/panel.h b/src/ui/panel.h new file mode 100644 index 0000000..b29572d --- /dev/null +++ b/src/ui/panel.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +G_DECLARE_DERIVABLE_TYPE(Panel, panel, GINPUT, PANEL, AdwBin) + +G_BEGIN_DECLS + +/** + * PanelClass: + * + * The contents of this struct are private and should not be accessed directly. + */ +struct _PanelClass +{ + /*< private >*/ + AdwBinClass parent_class; + + const gchar* (*get_help_uri)(Panel* panel); + + GtkWidget* (*get_sidebar_widget)(Panel* panel); +}; + +GPermission* panel_get_permission(Panel* panel); + +const gchar* panel_get_help_uri(Panel* panel); + +GtkWidget* panel_get_sidebar_widget(Panel* panel); + +GCancellable* panel_get_cancellable(Panel* panel); + +gboolean panel_get_folded(Panel* panel); + +GtkWidget* panel_get_content(Panel* panel); + +void panel_set_content(Panel* panel, GtkWidget* content); + +GtkWidget* panel_get_titlebar(Panel* panel); + +void panel_set_titlebar(Panel* panel, GtkWidget* titlebar); + +void panel_deactivate(Panel* panel); + +G_END_DECLS diff --git a/src/ui/panels/empty_panel.c b/src/ui/panels/empty_panel.c new file mode 100644 index 0000000..67b92c1 --- /dev/null +++ b/src/ui/panels/empty_panel.c @@ -0,0 +1,30 @@ +#include "empty_panel.h" + +struct _EmptyPanel +{ + Panel parent_instance; +}; + +G_DEFINE_TYPE (EmptyPanel, empty_panel, panel_get_type()) + +static void +empty_panel_class_init(EmptyPanelClass* klass) +{ + GObjectClass* object_class = G_OBJECT_CLASS(klass); + GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(klass); + PanelClass* panel_class = GINPUT_PANEL_CLASS(klass); + + gtk_widget_class_set_template_from_resource(widget_class, "/v/ginput/panel-empty.ui"); +} + +static void +empty_panel_init(EmptyPanel* self) +{ + gtk_widget_init_template(GTK_WIDGET(self)); +} + +Panel* +empty_panel_new(void) +{ + return g_object_new(empty_panel_get_type(), NULL); +} diff --git a/src/ui/panels/empty_panel.h b/src/ui/panels/empty_panel.h new file mode 100644 index 0000000..b32b973 --- /dev/null +++ b/src/ui/panels/empty_panel.h @@ -0,0 +1,11 @@ +#pragma once + +#include "ui/panel.h" + +G_BEGIN_DECLS + +G_DECLARE_FINAL_TYPE(EmptyPanel, empty_panel, GINPUT, EMPTY_PANEL, Panel) + +G_END_DECLS + +Panel* empty_panel_new(void); diff --git a/ui/ginput.cmb b/ui/ginput.cmb new file mode 100644 index 0000000..45281c6 --- /dev/null +++ b/ui/ginput.cmb @@ -0,0 +1,74 @@ + + + + + (1,1,"gkeyboards.ui","main-window.ui",None,None,None,None,None,None," <menu id=\"primary_menu\">\n <section>\n <item>\n <attribute name=\"label\" translatable=\"yes\">About</attribute>\n <attribute name=\"action\">app.about</attribute>\n </item>\n </section>\n </menu>\n"), + (2,1,None,"panel.ui",None,None,None,None,None,None,None), + (3,None,None,"panel-empty.ui",None,None,None,None,None,None,"<template class=\"EmptyPanel\" parent=\"Panel\">\n <child type=\"content\">\n <object class=\"GtkLabel\">\n <property name=\"label\">Nothing to see here</property>\n </object>\n </child>\n</template>") + + + (1,1,"AdwApplicationWindow","MainWindow",None,None,None,None,None,None), + (1,2,"AdwLeaflet","main_leaflet",1,None,None,None,-1,None), + (1,3,"GtkBox","sidebar_box",2,None,None,None,None,None), + (1,4,"AdwHeaderBar","header",3,None,None,None,None,None), + (1,5,"AdwWindowTitle","sidebar_title_widget",4,None,None,None,None,None), + (1,6,"GtkMenuButton",None,4,None,"end",None,None,"<property name=\"menu-model\">primary_menu</property>"), + (1,7,"AdwLeafletPage",None,2,None,None,None,1,None), + (1,8,"GtkSeparator",None,7,None,None,None,None,None), + (1,9,"GtkBox","panel_box",2,None,None,None,2,None), + (1,10,"GtkStack","stack",9,None,None,None,None,None), + (1,11,"GtkScrolledWindow",None,3,None,None,None,1,None), + (1,12,"GtkViewport",None,11,None,None,None,None,None), + (2,1,"AdwBin","Panel",None,None,None,None,None,None), + (2,11,"GtkBox","main_box",1,None,None,None,None,None), + (2,12,"AdwBin","content_bin",11,None,None,None,1,None), + (2,13,"AdwBin","titlebar_bin",11,None,None,None,None,None), + (2,15,"AdwHeaderBar","titlebar",13,None,None,None,None,None), + (2,16,"AdwWindowTitle","title",15,None,None,None,None,None) + + + (1,1,"GtkWindow","default-height","980",None,None,None,None,None,None,None,None,None), + (1,1,"GtkWindow","default-width","640",None,None,None,None,None,None,None,None,None), + (1,3,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None), + (1,4,"AdwHeaderBar","show-end-title-buttons","False",None,None,None,None,None,None,None,None,None), + (1,4,"AdwHeaderBar","title-widget",None,None,None,None,None,5,None,None,None,None), + (1,5,"AdwWindowTitle","title","Input devices",None,None,None,None,None,None,None,None,None), + (1,6,"GtkMenuButton","icon-name","open-menu-symbolic",None,None,None,None,None,None,None,None,None), + (1,6,"GtkMenuButton","primary","True",None,None,None,None,None,None,None,None,None), + (1,7,"AdwLeafletPage","child",None,None,None,None,None,8,None,None,None,None), + (1,7,"AdwLeafletPage","navigatable","False",None,None,None,None,None,None,None,None,None), + (1,8,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None), + (1,9,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None), + (1,9,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None), + (1,9,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None), + (1,10,"GtkStack","transition-type","crossfade",None,None,None,None,None,None,None,None,None), + (1,10,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None), + (1,10,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None), + (1,10,"GtkWidget","width-request","360",None,None,None,None,None,None,None,None,None), + (1,11,"GtkScrolledWindow","child",None,None,None,None,None,12,None,None,None,None), + (1,11,"GtkScrolledWindow","hscrollbar-policy","never",None,None,None,None,None,None,None,None,None), + (1,11,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None), + (1,11,"GtkWidget","width-request","200",None,None,None,None,None,None,None,None,None), + (2,11,"GtkOrientable","orientation","vertical",None,None,None,None,None,None,None,None,None), + (2,11,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None), + (2,11,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None), + (2,12,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None), + (2,12,"GtkWidget","vexpand","True",None,None,None,None,None,None,None,None,None), + (2,13,"GtkWidget","hexpand","True",None,None,None,None,None,None,None,None,None), + (2,15,"AdwHeaderBar","show-start-title-buttons","False",None,None,None,None,None,None,None,None,None), + (2,15,"AdwHeaderBar","title-widget",None,None,None,None,None,16,None,None,None,None) + + + (1,6,"GtkWidget",1,1,None,None,None), + (1,10,"GtkWidget",1,1,None,None,None), + (1,10,"GtkWidget",2,2,None,1,None), + (1,8,"GtkWidget",1,1,None,None,None), + (1,8,"GtkWidget",2,2,None,1,None), + (1,6,"GtkWidget",2,2,None,1,None) + + + (1,10,"GtkWidget",2,2,"name","background"), + (1,8,"GtkWidget",2,2,"name","sidebar"), + (1,6,"GtkWidget",2,2,"name","image-button") + + diff --git a/ui/ginput.gresource.xml b/ui/ginput.gresource.xml new file mode 100644 index 0000000..0e487a6 --- /dev/null +++ b/ui/ginput.gresource.xml @@ -0,0 +1,9 @@ + + + + main-window.ui + panel.ui + panel-empty.ui + style.css + + diff --git a/ui/style.css b/ui/style.css new file mode 100644 index 0000000..9d821c8 --- /dev/null +++ b/ui/style.css @@ -0,0 +1 @@ +/* The style file for the ginput UI */ diff --git a/v.ginput.gschema.xml b/v.ginput.gschema.xml new file mode 100644 index 0000000..fb1760f --- /dev/null +++ b/v.ginput.gschema.xml @@ -0,0 +1,10 @@ + + + + + (980, 640, false) + Window state + Width, height, and folded values of the window + + +