diff --git a/client/meson.build b/client/meson.build index 2326bff..9cbcb77 100644 --- a/client/meson.build +++ b/client/meson.build @@ -9,6 +9,14 @@ project( add_project_arguments('-w', language: 'c') gtk_dep = dependency('gtk4') +gnome = import('gnome') + +resources = gnome.compile_resources( + 'resources', + 'resources.gresource.xml', + source_dir: '.', + c_name: 'resources' +) subdir('src') subdir('tests') diff --git a/client/resources.gresource.xml b/client/resources.gresource.xml new file mode 100644 index 0000000..85a271f --- /dev/null +++ b/client/resources.gresource.xml @@ -0,0 +1,6 @@ + + + + styles.css + + diff --git a/client/src/activities_view.vala b/client/src/activities_view.vala new file mode 100644 index 0000000..3c30249 --- /dev/null +++ b/client/src/activities_view.vala @@ -0,0 +1,86 @@ +namespace StudySystemClient { + private struct Activity { + public string subject; + public ActivityType type; + } + + enum ActivityType { + EXERCISES, + READING; + + public string to_string() { + switch (this) { + case EXERCISES: + return "Exercises"; + case READING: + return "Reading"; + default: + return "Invalid activity type"; + } + } + } + + public class ActivitiesView : Gtk.Box { + public ActivitiesView() { + margin_top = margin_bottom = margin_start = margin_end = 0; + + var scrolled_window = new Gtk.ScrolledWindow(); + scrolled_window.hscrollbar_policy = Gtk.PolicyType.NEVER; + scrolled_window.vexpand = true; + + var card_container = new Gtk.FlowBox(); + card_container.homogeneous = true; + card_container.min_children_per_line = 1; + card_container.max_children_per_line = 1; + card_container.selection_mode = Gtk.SelectionMode.NONE; + card_container.valign = Gtk.Align.START; + scrolled_window.add_css_class("card-container"); + + var activities = new Activity[] { + { "Linguistics", ActivityType.EXERCISES }, + { "Cybernetics", ActivityType.EXERCISES }, + { "Linguistics", ActivityType.READING }, + { "Physics", ActivityType.READING }, + { "Cybernetics", ActivityType.READING }, + { "Physics", ActivityType.EXERCISES }, + }; + foreach (var activity in activities) + card_container.append(new ActivityCard(activity)); + + scrolled_window.set_child(card_container); + this.append(scrolled_window); + } + } + + private class ActivityCard : Gtk.Frame { + public ActivityCard(Activity activity) { + add_css_class("card"); + + var content = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 12); + + var text = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); + text.hexpand = true; + + var subject = new Gtk.Label(activity.subject); + subject.halign = Gtk.Align.START; + subject.add_css_class("activity-subject"); + text.append(subject); + + var type = new Gtk.Label(activity.type.to_string()); + type.halign = Gtk.Align.START; + text.append(type); + + content.append(text); + + var button + = new Gtk.Button.from_icon_name("appointment-new-symbolic"); + button.vexpand = false; + button.valign = Gtk.Align.CENTER; + button.set_tooltip_text("Log session"); + button.add_css_class("log-session-button"); + content.append(button); + + set_child(content); + } + } +} diff --git a/client/src/main.vala b/client/src/main.vala index 37a4944..a3dca6b 100644 --- a/client/src/main.vala +++ b/client/src/main.vala @@ -1,5 +1,3 @@ -using Gtk; - namespace StudySystemClient { public class App : Gtk.Application { public App() { @@ -8,11 +6,18 @@ namespace StudySystemClient { protected override void activate() { try { - var connection = new Connection(Config.CERT_DIR); - new MainWindow(this, connection); + var css_provider = new Gtk.CssProvider(); + css_provider.load_from_resource( + "/sh/wip/studysystemclient/styles.css"); + Gtk.StyleContext.add_provider_for_display( + Gdk.Display.get_default(), + css_provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + + new MainWindow(this, new Connection(Config.CERT_DIR)); } catch (Error e) { - stderr.printf("Failed to initialize connection: %s\n", - e.message); + stderr.printf("Failed to initialize: %s\n", e.message); + return; } } } diff --git a/client/src/main_window.vala b/client/src/main_window.vala index 0a63bd5..9eeede7 100644 --- a/client/src/main_window.vala +++ b/client/src/main_window.vala @@ -1,10 +1,6 @@ namespace StudySystemClient { - const string title = "Study System Client"; - public class MainWindow : Gtk.ApplicationWindow { private Connection connection; - private Gtk.Button send_button; - private Gtk.Label response_label; public MainWindow(Gtk.Application app, Connection connection) { Object(application: app); @@ -13,30 +9,15 @@ namespace StudySystemClient { default_height = 580; this.connection = connection; - connection.received.connect((msg) => { - var der = Der.decode(msg) as Der.Choice; - var str = der.value as Der.Utf8String; - response_label.label = "Response: " + str.value; - }); - var box = new Gtk.Box(Gtk.Orientation.VERTICAL, 10); - box.margin_start = 10; - box.margin_end = 10; - box.margin_top = 10; - box.margin_bottom = 10; + var header_bar = new Gtk.HeaderBar(); + var title = new Gtk.Label("Study System Client"); + title.add_css_class("title"); + header_bar.title_widget = title; + set_titlebar(header_bar); - send_button = new Gtk.Button.with_label("Send"); - send_button.clicked.connect(() => { - var msg = new Der.Choice(0, new Der.Null()); - connection.send(msg.encode()); - }); - box.append(send_button); - - response_label = new Gtk.Label(""); - response_label.wrap = true; - box.append(response_label); - - set_child(box); + var activities_view = new ActivitiesView(); + set_child(activities_view); present(); } diff --git a/client/src/meson.build b/client/src/meson.build index 570b59c..734da92 100644 --- a/client/src/meson.build +++ b/client/src/meson.build @@ -10,12 +10,14 @@ configure_file( lib = library( 'study-system-client', sources: files( + 'activities_view.vala', 'connection.vala', 'der.vala', 'main_window.vala', - ), + ) + resources, dependencies: [gtk_dep], - vala_vapi: 'study-system-client.vapi' + vala_vapi: 'study-system-client.vapi', + vala_args: ['--pkg', 'gtk4'] ) lib_dep = declare_dependency( link_with: lib, diff --git a/client/styles.css b/client/styles.css new file mode 100644 index 0000000..56c55ca --- /dev/null +++ b/client/styles.css @@ -0,0 +1,25 @@ +.card-container { + background-color: mix(@theme_base_color, @theme_bg_color, 0.7); + padding: 6px; +} + +.card { + border: 1px solid alpha(@theme_fg_color, 0.2); + box-shadow: 0 1px 2px alpha(black, 0.15); + background-color: @theme_bg_color; + padding: 12px 16px; +} + +.activity-subject { + font-weight: bold; +} + +/* + * The visual center (i.e. the center of the clock) of the + * "appointment-new-symbolic" icon is slightly displaced from the + * center of the actual image, so tweak it here. + */ +.log-session-button image { + margin-top: -2px; + margin-left: 3px; +}