summaryrefslogtreecommitdiff
path: root/Userland/Demos
diff options
context:
space:
mode:
authorsin-ack <sin-ack@users.noreply.github.com>2021-08-16 22:09:58 +0000
committerAli Mohammad Pur <Ali.mpfard@gmail.com>2021-08-23 12:25:26 +0430
commitb30b7de2d2df940a864c028786325d606916feea (patch)
tree7e8d6d013b2aee5bcb90feb30985674be0663f9e /Userland/Demos
parenta8967388d3b6986dd10fab193c0b1fef287a4e18 (diff)
downloadserenity-b30b7de2d2df940a864c028786325d606916feea.zip
ModelGallery: Add the new Model Gallery application :^)
This is an application analogous to WidgetGallery, in that it tests various capabilities of LibGUI models. Right now it is pretty bare, but as more work towards LibGUI models is done regarding persistent model indices, more demos will be added.
Diffstat (limited to 'Userland/Demos')
-rw-r--r--Userland/Demos/CMakeLists.txt1
-rw-r--r--Userland/Demos/ModelGallery/BasicModel.cpp68
-rw-r--r--Userland/Demos/ModelGallery/BasicModel.h41
-rw-r--r--Userland/Demos/ModelGallery/BasicModelTab.gml44
-rw-r--r--Userland/Demos/ModelGallery/CMakeLists.txt17
-rw-r--r--Userland/Demos/ModelGallery/GalleryWidget.cpp75
-rw-r--r--Userland/Demos/ModelGallery/GalleryWidget.h38
-rw-r--r--Userland/Demos/ModelGallery/main.cpp39
8 files changed, 323 insertions, 0 deletions
diff --git a/Userland/Demos/CMakeLists.txt b/Userland/Demos/CMakeLists.txt
index 3fe8445740..f75aa4adc7 100644
--- a/Userland/Demos/CMakeLists.txt
+++ b/Userland/Demos/CMakeLists.txt
@@ -5,6 +5,7 @@ add_subdirectory(Fire)
add_subdirectory(LibGfxDemo)
add_subdirectory(LibGfxScaleDemo)
add_subdirectory(Mandelbrot)
+add_subdirectory(ModelGallery)
add_subdirectory(Mouse)
add_subdirectory(Screensaver)
add_subdirectory(Starfield)
diff --git a/Userland/Demos/ModelGallery/BasicModel.cpp b/Userland/Demos/ModelGallery/BasicModel.cpp
new file mode 100644
index 0000000000..10b76e37af
--- /dev/null
+++ b/Userland/Demos/ModelGallery/BasicModel.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "BasicModel.h"
+
+GUI::Variant BasicModel::data(GUI::ModelIndex const& index, GUI::ModelRole role) const
+{
+ if (role != GUI::ModelRole::Display)
+ return {};
+ if (!is_within_range(index))
+ return {};
+
+ return m_items.at(index.row());
+}
+
+TriState BasicModel::data_matches(GUI::ModelIndex const& index, GUI::Variant const& data) const
+{
+ if (!is_within_range(index))
+ return TriState::False;
+ if (!data.is_string())
+ return TriState::False;
+
+ auto& value = m_items.at(index.row());
+ return value.contains(data.as_string()) ? TriState::True : TriState::False;
+}
+
+void BasicModel::invalidate()
+{
+ Model::invalidate();
+ if (on_invalidate)
+ on_invalidate();
+}
+
+GUI::ModelIndex BasicModel::index(int row, int column, GUI::ModelIndex const& parent) const
+{
+ if (column != 0)
+ return {};
+ if (parent.is_valid())
+ return {};
+ if (row < 0 || row >= static_cast<int>(m_items.size()))
+ return {};
+
+ return create_index(row, column);
+}
+
+void BasicModel::add_item(String const& item)
+{
+ begin_insert_rows({}, m_items.size(), m_items.size());
+ m_items.append(item);
+ end_insert_rows();
+
+ did_update(UpdateFlag::DontInvalidateIndices);
+}
+
+void BasicModel::remove_item(GUI::ModelIndex const& index)
+{
+ if (!index.is_valid() || !is_within_range(index))
+ return;
+
+ begin_delete_rows({}, index.row(), index.row());
+ m_items.remove(index.row());
+ end_delete_rows();
+
+ did_update(UpdateFlag::DontInvalidateIndices);
+}
diff --git a/Userland/Demos/ModelGallery/BasicModel.h b/Userland/Demos/ModelGallery/BasicModel.h
new file mode 100644
index 0000000000..e0dfd1ed4c
--- /dev/null
+++ b/Userland/Demos/ModelGallery/BasicModel.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/Function.h>
+#include <AK/NonnullRefPtr.h>
+#include <AK/Vector.h>
+#include <LibGUI/Model.h>
+
+class BasicModel final : public GUI::Model {
+public:
+ static NonnullRefPtr<BasicModel> create()
+ {
+ return adopt_ref(*new BasicModel());
+ }
+
+ virtual int row_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return m_items.size(); }
+ virtual int column_count(GUI::ModelIndex const& = GUI::ModelIndex()) const override { return 1; }
+ virtual String column_name(int) const override { return "Item"; }
+
+ virtual GUI::Variant data(GUI::ModelIndex const&, GUI::ModelRole = GUI::ModelRole::Display) const override;
+ virtual TriState data_matches(GUI::ModelIndex const&, GUI::Variant const&) const override;
+ virtual void invalidate() override;
+ virtual GUI::ModelIndex index(int row, int column = 0, GUI::ModelIndex const& parent = GUI::ModelIndex()) const override;
+
+ Function<void()> on_invalidate;
+
+ void add_item(String const& item);
+ void remove_item(GUI::ModelIndex const&);
+
+private:
+ BasicModel()
+ {
+ }
+
+ Vector<String> m_items;
+};
diff --git a/Userland/Demos/ModelGallery/BasicModelTab.gml b/Userland/Demos/ModelGallery/BasicModelTab.gml
new file mode 100644
index 0000000000..f645563a52
--- /dev/null
+++ b/Userland/Demos/ModelGallery/BasicModelTab.gml
@@ -0,0 +1,44 @@
+@GUI::Widget {
+ name: "basic_model_tab"
+ layout: @GUI::VerticalBoxLayout {
+ margins: [4]
+ }
+
+ @GUI::Label {
+ text: "Here is a basic model, displayed on a table widget. Its clients are updated via granular updates. You can add or remove items with the widgets below."
+ text_alignment: "CenterLeft"
+
+ thickness: 2
+ fixed_height: 34
+ }
+
+ @GUI::TableView {
+ name: "model_table"
+ }
+
+ @GUI::Widget {
+ layout: @GUI::HorizontalBoxLayout {
+ }
+
+ fixed_height: 30
+
+ @GUI::TextBox {
+ name: "new_item_name"
+ placeholder: "Enter some text to be added..."
+ }
+
+ @GUI::Button {
+ name: "add_new_item"
+ fixed_width: 22
+ fixed_height: 22
+ tooltip: "Add the text as an item to the model"
+ }
+
+ @GUI::Button {
+ name: "remove_selected_item"
+ fixed_width: 22
+ fixed_height: 22
+ tooltip: "Remove the selected item from the model"
+ }
+ }
+}
diff --git a/Userland/Demos/ModelGallery/CMakeLists.txt b/Userland/Demos/ModelGallery/CMakeLists.txt
new file mode 100644
index 0000000000..cf9f7f9c96
--- /dev/null
+++ b/Userland/Demos/ModelGallery/CMakeLists.txt
@@ -0,0 +1,17 @@
+serenity_component(
+ ModelGallery
+ TARGETS ModelGallery
+)
+
+compile_gml(./BasicModelTab.gml BasicModelTabGML.h basic_model_tab_gml)
+
+set(SOURCES
+ main.cpp
+ GalleryWidget.cpp
+ BasicModel.cpp
+ BasicModelTabGML.h
+)
+
+serenity_app(ModelGallery ICON app-model-gallery)
+
+target_link_libraries(ModelGallery LibGUI LibGfx)
diff --git a/Userland/Demos/ModelGallery/GalleryWidget.cpp b/Userland/Demos/ModelGallery/GalleryWidget.cpp
new file mode 100644
index 0000000000..28c77ce1f4
--- /dev/null
+++ b/Userland/Demos/ModelGallery/GalleryWidget.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "GalleryWidget.h"
+#include <Demos/ModelGallery/BasicModelTabGML.h>
+
+GalleryWidget::GalleryWidget()
+{
+ set_fill_with_background_color(true);
+ set_layout<GUI::VerticalBoxLayout>();
+
+ auto& inner_widget = add<GUI::Widget>();
+ auto& inner_layout = inner_widget.set_layout<GUI::VerticalBoxLayout>();
+ inner_layout.set_margins({ 4 });
+
+ m_tab_widget = inner_widget.add<GUI::TabWidget>();
+ m_statusbar = add<GUI::Statusbar>();
+
+ load_basic_model_tab();
+ load_sorting_filtering_tab();
+}
+
+void GalleryWidget::load_basic_model_tab()
+{
+ auto& tab = m_tab_widget->add_tab<GUI::Widget>("Basic Model");
+ tab.load_from_gml(basic_model_tab_gml);
+
+ m_basic_model = BasicModel::create();
+ m_basic_model_table = *tab.find_descendant_of_type_named<GUI::TableView>("model_table");
+ m_basic_model_table->set_model(m_basic_model);
+
+ m_basic_model->on_invalidate = [&] {
+ m_invalidation_count++;
+ m_statusbar->set_text(String::formatted("Times invalidated: {}", m_invalidation_count));
+ };
+
+ m_statusbar->set_text(String::formatted("Times invalidated: {}", m_invalidation_count));
+
+ m_basic_model->add_item("Well...");
+ m_basic_model->add_item("...hello...");
+ m_basic_model->add_item("...friends! :^)");
+
+ m_new_item_name = *tab.find_descendant_of_type_named<GUI::TextBox>("new_item_name");
+ m_add_new_item = *tab.find_descendant_of_type_named<GUI::Button>("add_new_item");
+ m_remove_selected_item = *tab.find_descendant_of_type_named<GUI::Button>("remove_selected_item");
+
+ m_add_new_item->set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/plus.png"));
+ m_remove_selected_item->set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/minus.png"));
+
+ m_new_item_name->on_return_pressed = [&] { add_textbox_contents_to_basic_model(); };
+ m_add_new_item->on_click = [&](auto) { add_textbox_contents_to_basic_model(); };
+
+ m_remove_selected_item->on_click = [&](auto) {
+ auto index = m_basic_model_table->cursor_index();
+ if (index.is_valid()) {
+ m_basic_model->remove_item(index);
+ }
+ };
+}
+
+void GalleryWidget::load_sorting_filtering_tab()
+{
+ // TODO: Add the SortingFilteringProxyModel here.
+}
+
+void GalleryWidget::add_textbox_contents_to_basic_model()
+{
+ if (!m_new_item_name->current_line().is_empty()) {
+ m_basic_model->add_item(m_new_item_name->current_line().to_utf8());
+ m_new_item_name->set_text("");
+ }
+}
diff --git a/Userland/Demos/ModelGallery/GalleryWidget.h b/Userland/Demos/ModelGallery/GalleryWidget.h
new file mode 100644
index 0000000000..ddb7a3c0b6
--- /dev/null
+++ b/Userland/Demos/ModelGallery/GalleryWidget.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include "BasicModel.h"
+#include <LibGUI/BoxLayout.h>
+#include <LibGUI/Button.h>
+#include <LibGUI/Statusbar.h>
+#include <LibGUI/TabWidget.h>
+#include <LibGUI/TableView.h>
+#include <LibGUI/TextBox.h>
+#include <LibGUI/Widget.h>
+
+class GalleryWidget final : public GUI::Widget {
+ C_OBJECT(GalleryWidget)
+
+private:
+ GalleryWidget();
+
+ void load_basic_model_tab();
+ void load_sorting_filtering_tab();
+
+ void add_textbox_contents_to_basic_model();
+
+ RefPtr<GUI::TabWidget> m_tab_widget;
+ RefPtr<GUI::Statusbar> m_statusbar;
+
+ size_t m_invalidation_count { 0 };
+ RefPtr<BasicModel> m_basic_model;
+ RefPtr<GUI::TableView> m_basic_model_table;
+ RefPtr<GUI::TextBox> m_new_item_name;
+ RefPtr<GUI::Button> m_add_new_item;
+ RefPtr<GUI::Button> m_remove_selected_item;
+};
diff --git a/Userland/Demos/ModelGallery/main.cpp b/Userland/Demos/ModelGallery/main.cpp
new file mode 100644
index 0000000000..acdf0c5876
--- /dev/null
+++ b/Userland/Demos/ModelGallery/main.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021, sin-ack <sin-ack@protonmail.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include "GalleryWidget.h"
+#include <LibGUI/Application.h>
+#include <LibGUI/BoxLayout.h>
+#include <LibGUI/Button.h>
+#include <LibGUI/Frame.h>
+#include <LibGUI/MessageBox.h>
+#include <unistd.h>
+
+int main(int argc, char** argv)
+{
+ if (pledge("stdio recvfd sendfd rpath wpath cpath unix", nullptr) < 0) {
+ perror("pledge");
+ return 1;
+ }
+
+ auto app = GUI::Application::construct(argc, argv);
+
+ if (pledge("stdio recvfd sendfd rpath", nullptr) < 0) {
+ perror("pledge");
+ return 1;
+ }
+
+ auto app_icon = GUI::Icon::default_icon("app-model-gallery");
+
+ auto window = GUI::Window::construct();
+ window->set_title("Model Gallery");
+ window->set_icon(app_icon.bitmap_for_size(16));
+ window->resize(430, 480);
+ window->set_main_widget<GalleryWidget>();
+
+ window->show();
+ return app->exec();
+}