# HG changeset patch
# Node ID f1eff3e4609a
# Parent  ab603eb43724
diff --git a/gplugin-gtk4/data/gplugin-gtk.gresource.xml b/gplugin-gtk4/data/gplugin-gtk.gresource.xml
--- a/gplugin-gtk4/data/gplugin-gtk.gresource.xml
+++ b/gplugin-gtk4/data/gplugin-gtk.gresource.xml
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
   <gresource prefix="/org/imfreedom/keep/gplugin/gplugin-gtk/">
+    <file compressed="true">plugin-page.ui</file>
     <file compressed="true">plugin-row.ui</file>
     <file compressed="true">view.ui</file>
   </gresource>
diff --git a/gplugin-gtk4/data/plugin-page.ui b/gplugin-gtk4/data/plugin-page.ui
new file mode 100644
--- /dev/null
+++ b/gplugin-gtk4/data/plugin-page.ui
@@ -0,0 +1,451 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk" version="4.0"/>
+  <template class="GPluginGtkPluginPage" parent="GtkBox">
+    <property name="orientation">vertical</property>
+    <property name="spacing">12</property>
+    <child>
+      <object class="GtkBox">
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <property name="valign">center</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="css-classes">title-1</property>
+                <property name="hexpand">1</property>
+                <binding name="label">
+                  <closure type="gchararray" function="gplugin_gtk_lookup_plugin_name">
+                    <lookup name="info" type="GPluginPlugin">
+                      <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                    </lookup>
+                    <lookup name="filename" type="GPluginPlugin">
+                      <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                    </lookup>
+                  </closure>
+                </binding>
+                <property name="wrap">1</property>
+                <property name="wrap-mode">word-char</property>
+                <property name="xalign">0</property>
+                <property name="yalign">0</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkSwitch">
+                <property name="halign">end</property>
+                <property name="valign">center</property>
+                <binding name="sensitive">
+                  <closure type="gboolean" function="gplugin_gtk_lookup_plugin_state_sensitivity">
+                    <lookup name="state" type="GPluginPlugin">
+                      <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                    </lookup>
+                  </closure>
+                </binding>
+                <binding name="state">
+                  <closure type="gboolean" function="gplugin_gtk_lookup_plugin_state">
+                    <lookup name="state" type="GPluginPlugin">
+                      <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                    </lookup>
+                  </closure>
+                </binding>
+                <signal name="state-set" handler="gplugin_gtk_plugin_page_enable_state_set_cb"/>
+              </object>
+            </child>
+          </object>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="orientation">horizontal</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="css-classes">dim-label</property>
+                <property name="halign">start</property>
+                <property name="hexpand">1</property>
+                <binding name="label">
+                  <lookup name="summary" type="GPluginPluginInfo">
+                    <lookup name="info" type="GPluginPlugin">
+                      <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                    </lookup>
+                  </lookup>
+                </binding>
+                <property name="wrap">1</property>
+                <property name="wrap-mode">word-char</property>
+                <property name="ellipsize">end</property>
+                <property name="xalign">0</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel">
+                <property name="css-classes">dim-label</property>
+                <property name="halign">end</property>
+                <binding name="label">
+                  <lookup name="version" type="GPluginPluginInfo">
+                    <lookup name="info" type="GPluginPlugin">
+                      <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                    </lookup>
+                  </lookup>
+                </binding>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
+    <child>
+      <object class="GtkLabel" id="description">
+        <binding name="label">
+          <lookup name="description" type="GPluginPluginInfo">
+            <lookup name="info" type="GPluginPlugin">
+              <lookup name="plugin">GPluginGtkPluginPage</lookup>
+            </lookup>
+          </lookup>
+        </binding>
+        <property name="wrap">1</property>
+        <property name="xalign">0</property>
+      </object>
+    </child>
+    <child>
+      <object class="GtkFrame">
+        <property name="child">
+          <object class="GtkListBox">
+            <property name="css-classes">rich-list</property>
+            <property name="selection-mode">none</property>
+            <property name="show-separators">1</property>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="authors-label">
+                        <property name="label" translatable="1">Authors</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <binding name="label">
+                          <closure type="gchararray" function="gplugin_gtk_plugin_page_newline_strjoinv_cb">
+                            <lookup name="authors" type="GPluginPluginInfo">
+                              <lookup name="info" type="GPluginPlugin">
+                                <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                              </lookup>
+                            </lookup>
+                          </closure>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">authors-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="website-label">
+                        <property name="label" translatable="1">Website</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="use-markup">1</property>
+                        <binding name="label">
+                          <closure type="gchararray" function="gplugin_gtk_plugin_page_lookup_website_cb">
+                            <lookup name="website" type="GPluginPluginInfo">
+                              <lookup name="info" type="GPluginPlugin">
+                                <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                              </lookup>
+                            </lookup>
+                          </closure>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">website-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="dependencies-label">
+                        <property name="label" translatable="1">Dependencies</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <binding name="label">
+                          <closure type="gchararray" function="gplugin_gtk_plugin_page_newline_strjoinv_cb">
+                            <lookup name="dependencies" type="GPluginPluginInfo">
+                              <lookup name="info" type="GPluginPlugin">
+                                <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                              </lookup>
+                            </lookup>
+                          </closure>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">dependencies-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="error-label">
+                        <property name="label" translatable="1">Error</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <property name="wrap">1</property>
+                        <binding name="label">
+                          <closure type="gchararray" function="gplugin_gtk_plugin_page_lookup_error_cb">
+                            <lookup name="error" type="GPluginPlugin">
+                              <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                            </lookup>
+                          </closure>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">error-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="id-label">
+                        <property name="label" translatable="1">ID</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <binding name="label">
+                          <lookup name="id" type="GPluginPluginInfo">
+                            <lookup name="info" type="GPluginPlugin">
+                              <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                            </lookup>
+                          </lookup>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">id-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="filename-label">
+                        <property name="label" translatable="1">Filename</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <property name="ellipsize">start</property>
+                        <binding name="label">
+                          <lookup name="filename" type="GPluginPlugin">
+                            <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                          </lookup>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">filename-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="abi-version-label">
+                        <property name="label" translatable="1">ABI Version</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <binding name="label">
+                          <closure type="gchararray" function="gplugin_gtk_plugin_page_lookup_abi_version_cb">
+                            <lookup name="abi-version" type="GPluginPluginInfo">
+                              <lookup name="info" type="GPluginPlugin">
+                                <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                              </lookup>
+                            </lookup>
+                          </closure>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">abi-version-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="loader-label">
+                        <property name="label" translatable="1">Loader</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkLabel">
+                        <property name="selectable">1</property>
+                        <binding name="label">
+                          <closure type="gchararray" function="gplugin_gtk_plugin_page_lookup_loader_cb">
+                            <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                          </closure>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">loader-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="internal-label">
+                        <property name="label" translatable="1">Internal</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="icon-name">object-select-symbolic</property>
+                        <binding name="visible">
+                          <lookup name="internal" type="GPluginPluginInfo">
+                            <lookup name="info" type="GPluginPlugin">
+                              <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                            </lookup>
+                          </lookup>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">internal-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkListBoxRow">
+                <property name="activatable">0</property>
+                <property name="child">
+                  <object class="GtkBox">
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkLabel" id="load-on-query-label">
+                        <property name="label" translatable="1">Load on Query</property>
+                        <property name="xalign">0</property>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkImage">
+                        <property name="icon-name">object-select-symbolic</property>
+                        <binding name="visible">
+                          <lookup name="load-on-query" type="GPluginPluginInfo">
+                            <lookup name="info" type="GPluginPlugin">
+                              <lookup name="plugin">GPluginGtkPluginPage</lookup>
+                            </lookup>
+                          </lookup>
+                        </binding>
+                        <accessibility>
+                          <relation name="labelled-by">load-on-query-label</relation>
+                        </accessibility>
+                      </object>
+                    </child>
+                  </object>
+                </property>
+              </object>
+            </child>
+          </object>
+        </property>
+      </object>
+    </child>
+  </template>
+  <object class="GtkSizeGroup">
+    <property name="mode">horizontal</property>
+    <widgets>
+      <widget name="authors-label"/>
+      <widget name="website-label"/>
+      <widget name="dependencies-label"/>
+      <widget name="error-label"/>
+      <widget name="id-label"/>
+      <widget name="filename-label"/>
+      <widget name="abi-version-label"/>
+      <widget name="loader-label"/>
+      <widget name="internal-label"/>
+      <widget name="load-on-query-label"/>
+    </widgets>
+  </object>
+</interface>
diff --git a/gplugin-gtk4/data/plugin-row.ui b/gplugin-gtk4/data/plugin-row.ui
--- a/gplugin-gtk4/data/plugin-row.ui
+++ b/gplugin-gtk4/data/plugin-row.ui
@@ -94,333 +94,12 @@
               </object>
             </child>
             <child>
-              <object class="GtkButton" id="config">
-                <property name="css-classes">circular</property>
-                <property name="receives-default">1</property>
-                <property name="valign">center</property>
-                <property name="child">
-                  <object class="GtkImage">
-                    <property name="margin-start">6</property>
-                    <property name="margin-end">6</property>
-                    <property name="margin-top">6</property>
-                    <property name="margin-bottom">6</property>
-                    <property name="icon-name">preferences-system-symbolic</property>
-                  </object>
-                </property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkSeparator">
-                <property name="orientation">vertical</property>
-              </object>
-            </child>
-            <child>
-              <object class="GtkExpander" id="expander">
-                <property name="halign">center</property>
-                <property name="valign">center</property>
+              <object class="GtkImage">
+                <property name="icon-name">go-next-symbolic</property>
               </object>
             </child>
           </object>
         </child>
-        <child>
-          <object class="GtkRevealer" id="revealer">
-            <property name="reveal-child" bind-source="expander" bind-property="expanded" bind-flags="bidirectional|sync-create"/>
-            <property name="child">
-              <object class="GtkBox">
-                <property name="orientation">vertical</property>
-                <property name="spacing">12</property>
-                <child>
-                  <object class="GtkLabel" id="description">
-                    <property name="margin-start">12</property>
-                    <property name="margin-end">12</property>
-                    <property name="margin-top">12</property>
-                    <property name="margin-bottom">12</property>
-                    <property name="label">A longer description of what this plugin does.</property>
-                    <property name="wrap">1</property>
-                    <property name="xalign">0</property>
-                  </object>
-                </child>
-                <child>
-                  <object class="GtkListBox">
-                    <property name="css-classes">nested</property>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="authors_label">
-                                <property name="label" translatable="1">Authors</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkBox" id="authors_box">
-                                <property name="orientation">vertical</property>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="label">An Author &lt;author@example.com&gt;</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="website_label">
-                                <property name="label" translatable="1">Website</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="website">
-                                <property name="label">&lt;a href=&quot;https://pidgin.im&quot;&gt;https://pidgin.im&lt;/a&gt;</property>
-                                <property name="use-markup">1</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="dependencies_label">
-                                <property name="label" translatable="1">Dependencies</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkBox" id="dependencies_box">
-                                <property name="orientation">vertical</property>
-                                <child>
-                                  <object class="GtkLabel">
-                                    <property name="label">(none)</property>
-                                  </object>
-                                </child>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="error_label">
-                                <property name="label" translatable="1">Error</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="error">
-                                <property name="label">(none)</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="id_label">
-                                <property name="label" translatable="1">ID</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="id">
-                                <property name="label">gplugin/example-plugin</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="filename_label">
-                                <property name="label" translatable="1">Filename</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="filename">
-                                <property name="label">/the/full/path/to/plugin.so</property>
-                                <property name="ellipsize">start</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="abi_version_label">
-                                <property name="label" translatable="1">ABI Version</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="abi_version">
-                                <property name="label">DEADC0DE</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="loader_label">
-                                <property name="label" translatable="1">Loader</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="loader">
-                                <property name="label">GPluginExampleLoader</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="internal_label">
-                                <property name="label" translatable="1">Internal</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="internal">
-                                <property name="label">Maybe</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkListBoxRow">
-                        <property name="activatable">0</property>
-                        <property name="selectable">0</property>
-                        <property name="child">
-                          <object class="GtkBox">
-                            <property name="margin-start">12</property>
-                            <property name="margin-end">12</property>
-                            <property name="margin-top">12</property>
-                            <property name="margin-bottom">12</property>
-                            <property name="spacing">12</property>
-                            <child>
-                              <object class="GtkLabel" id="load_on_query_label">
-                                <property name="label" translatable="1">Load on Query</property>
-                                <property name="xalign">0</property>
-                              </object>
-                            </child>
-                            <child>
-                              <object class="GtkLabel" id="load_on_query">
-                                <property name="label">Maybe</property>
-                              </object>
-                            </child>
-                          </object>
-                        </property>
-                      </object>
-                    </child>
-                  </object>
-                </child>
-              </object>
-            </property>
-          </object>
-        </child>
       </object>
     </property>
     <style>
@@ -428,19 +107,4 @@
       <class name="expander"/>
     </style>
   </template>
-  <object class="GtkSizeGroup">
-    <property name="mode">horizontal</property>
-    <widgets>
-      <widget name="authors_label"/>
-      <widget name="website_label"/>
-      <widget name="dependencies_label"/>
-      <widget name="error_label"/>
-      <widget name="id_label"/>
-      <widget name="filename_label"/>
-      <widget name="abi_version_label"/>
-      <widget name="loader_label"/>
-      <widget name="internal_label"/>
-      <widget name="load_on_query_label"/>
-    </widgets>
-  </object>
 </interface>
diff --git a/gplugin-gtk4/data/view.ui b/gplugin-gtk4/data/view.ui
--- a/gplugin-gtk4/data/view.ui
+++ b/gplugin-gtk4/data/view.ui
@@ -2,46 +2,96 @@
 <interface>
   <requires lib="gtk" version="4.0"/>
   <template class="GPluginGtkView" parent="GtkBox">
-    <property name="orientation">vertical</property>
     <child>
-      <object class="GtkSearchBar" id="search_bar">
-        <property name="key-capture-widget">list_box</property>
-        <property name="show-close-button">1</property>
+      <object class="GtkStack" id="stack">
+        <property name="transition-type">slide-left-right</property>
+        <property name="vhomogeneous">0</property>
         <child>
-          <object class="GtkSearchEntry" id="search_entry">
-            <signal name="search-changed" handler="gplugin_gtk_view_search_changed"/>
+          <object class="GtkStackPage">
+            <property name="name">overview</property>
+            <property name="child">
+              <object class="GtkScrolledWindow">
+                <property name="hscrollbar_policy">never</property>
+                <property name="vexpand">1</property>
+                <child>
+                  <object class="GtkBox">
+                    <property name="orientation">vertical</property>
+                    <child>
+                      <object class="GtkSearchBar" id="search_bar">
+                        <property name="key-capture-widget">list_box</property>
+                        <property name="show-close-button">1</property>
+                        <child>
+                          <object class="GtkSearchEntry" id="search_entry">
+                            <signal name="search-changed" handler="gplugin_gtk_view_search_changed"/>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GtkFrame">
+                        <property name="margin-bottom">24</property>
+                        <property name="margin-end">24</property>
+                        <property name="margin-start">24</property>
+                        <property name="margin-top">24</property>
+                        <child>
+                          <object class="GtkListBox" id="list_box">
+                            <property name="css-classes">rich-list</property>
+                            <property name="selection-mode">none</property>
+                            <property name="show-separators">1</property>
+                            <signal name="row-activated" handler="gplugin_gtk_view_row_activated"/>
+                            <child type="placeholder">
+                              <object class="GtkLabel">
+                                <property name="css-classes">large-title</property>
+                                <property name="label" translatable="1">No plugins found</property>
+                                <property name="margin-bottom">24</property>
+                                <property name="margin-end">24</property>
+                                <property name="margin-start">24</property>
+                                <property name="margin-top">24</property>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </property>
           </object>
         </child>
-      </object>
-    </child>
-    <child>
-      <object class="GtkScrolledWindow">
-        <property name="hscrollbar_policy">never</property>
-        <property name="vexpand">1</property>
         <child>
-          <object class="GtkFrame">
-            <property name="margin-bottom">24</property>
-            <property name="margin-end">24</property>
-            <property name="margin-start">24</property>
-            <property name="margin-top">24</property>
-            <child>
-              <object class="GtkListBox" id="list_box">
-                <property name="css-classes">rich-list</property>
-                <property name="selection-mode">none</property>
-                <property name="show-separators">1</property>
-                <signal name="row-activated" handler="gplugin_gtk_view_row_activated"/>
-                <child type="placeholder">
-                  <object class="GtkLabel">
-                    <property name="css-classes">large-title</property>
-                    <property name="label" translatable="1">No plugins found</property>
+          <object class="GtkStackPage">
+            <property name="name">plugin-page</property>
+            <property name="child">
+              <object class="GtkScrolledWindow">
+                <property name="hscrollbar_policy">never</property>
+                <property name="vexpand">1</property>
+                <child>
+                  <object class="GtkBox">
                     <property name="margin-bottom">24</property>
                     <property name="margin-end">24</property>
                     <property name="margin-start">24</property>
                     <property name="margin-top">24</property>
+                    <property name="orientation">horizontal</property>
+                    <property name="spacing">12</property>
+                    <child>
+                      <object class="GtkButton">
+                        <property name="css-classes">flat</property>
+                        <property name="halign">start</property>
+                        <property name="valign">start</property>
+                        <property name="icon-name">go-previous-symbolic</property>
+                        <signal name="clicked" handler="gplugin_gtk_view_back_clicked_cb" object="GPluginGtkView" swapped="no"/>
+                      </object>
+                    </child>
+                    <child>
+                      <object class="GPluginGtkPluginPage" id="plugin_page">
+                        <signal name="plugin-state-set" handler="gplugin_gtk_view_plugin_state_set_cb" object="GPluginGtkView" swapped="no"/>
+                      </object>
+                    </child>
                   </object>
                 </child>
               </object>
-            </child>
+            </property>
           </object>
         </child>
       </object>
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-page.h b/gplugin-gtk4/gplugin-gtk-plugin-page.h
new file mode 100644
--- /dev/null
+++ b/gplugin-gtk4/gplugin-gtk-plugin-page.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(GPLUGIN_GTK_GLOBAL_HEADER_INSIDE) && \
+	!defined(GPLUGIN_GTK_COMPILATION)
+#error "only <gplugin-gtk.h> may be included directly"
+#endif
+
+#ifndef GPLUGIN_GTK_PLUGIN_PAGE_H
+#define GPLUGIN_GTK_PLUGIN_PAGE_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+#include <gplugin.h>
+
+G_BEGIN_DECLS
+
+#define GPLUGIN_GTK_TYPE_PLUGIN_PAGE (gplugin_gtk_plugin_page_get_type())
+G_DECLARE_FINAL_TYPE(
+	GPluginGtkPluginPage,
+	gplugin_gtk_plugin_page,
+	GPLUGIN_GTK,
+	PLUGIN_PAGE,
+	GtkBox)
+
+GtkWidget *gplugin_gtk_plugin_page_new(void);
+
+void gplugin_gtk_plugin_page_set_plugin(
+	GPluginGtkPluginPage *page,
+	GPluginPlugin *plugin);
+GPluginPlugin *gplugin_gtk_plugin_page_get_plugin(GPluginGtkPluginPage *page);
+
+G_END_DECLS
+
+#endif /* GPLUGIN_GTK_PLUGIN_PAGE_H */
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-page.c b/gplugin-gtk4/gplugin-gtk-plugin-page.c
new file mode 100644
--- /dev/null
+++ b/gplugin-gtk4/gplugin-gtk-plugin-page.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2022 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <gplugin.h>
+
+#include <gplugin-gtk-plugin-closures.h>
+#include <gplugin-gtk-plugin-page.h>
+
+/**
+ * GPluginGtkPluginPage:
+ *
+ * A widget that displays a single [iface@GPlugin.Plugin] in a user friendly
+ * way, intended to be placed in a [class@GPluginGtk4.View].
+ */
+
+/******************************************************************************
+ * Structs
+ *****************************************************************************/
+struct _GPluginGtkPluginPage {
+	GtkBox parent;
+
+	GPluginPlugin *plugin;
+};
+
+/******************************************************************************
+ * Enums
+ *****************************************************************************/
+enum {
+	PROP_ZERO,
+	PROP_PLUGIN,
+	N_PROPERTIES,
+};
+static GParamSpec *properties[N_PROPERTIES] = {
+	NULL,
+};
+
+enum {
+	SIG_PLUGIN_STATE_SET,
+	N_SIGNALS,
+};
+static guint signals[N_SIGNALS] = {
+	0,
+};
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static gboolean
+gplugin_gtk_plugin_page_enable_state_set_cb(
+	G_GNUC_UNUSED GtkSwitch *widget,
+	gboolean state,
+	gpointer data)
+{
+	GPluginGtkPluginPage *page = GPLUGIN_GTK_PLUGIN_PAGE(data);
+
+	g_signal_emit(G_OBJECT(page), signals[SIG_PLUGIN_STATE_SET], 0, state);
+
+	return TRUE;
+}
+
+static gchar *
+gplugin_gtk_plugin_page_newline_strjoinv_cb(
+	G_GNUC_UNUSED GtkClosureExpression *expression,
+	gchar **str_array,
+	G_GNUC_UNUSED gpointer data)
+{
+	if(str_array == NULL) {
+		return g_strdup(_("(none)"));
+	}
+
+	return g_strjoinv("\n", str_array);
+}
+
+static gchar *
+gplugin_gtk_plugin_page_lookup_website_cb(
+	G_GNUC_UNUSED GtkClosureExpression *expression,
+	const gchar *website,
+	G_GNUC_UNUSED gpointer data)
+{
+	if(website == NULL) {
+		return NULL;
+	}
+
+	return g_markup_printf_escaped("<a href=\"%s\">%s</a>", website, website);
+}
+
+static gchar *
+gplugin_gtk_plugin_page_lookup_error_cb(
+	G_GNUC_UNUSED GtkClosureExpression *expression,
+	GError *error,
+	G_GNUC_UNUSED gpointer data)
+{
+	if(error == NULL) {
+		return g_strdup(_("(none)"));
+	}
+
+	return g_strdup(error->message);
+}
+
+static gchar *
+gplugin_gtk_plugin_page_lookup_abi_version_cb(
+	G_GNUC_UNUSED GtkClosureExpression *expression,
+	guint32 abi_version,
+	G_GNUC_UNUSED gpointer data)
+{
+	return g_strdup_printf("%08x", abi_version);
+}
+
+static gchar *
+gplugin_gtk_plugin_page_lookup_loader_cb(
+	G_GNUC_UNUSED GtkClosureExpression *expression,
+	GPluginPlugin *plugin,
+	G_GNUC_UNUSED gpointer data)
+{
+	if(GPLUGIN_IS_PLUGIN(plugin)) {
+		GPluginLoader *plugin_loader = gplugin_plugin_get_loader(plugin);
+
+		if(GPLUGIN_IS_LOADER(plugin_loader)) {
+			return g_strdup(G_OBJECT_TYPE_NAME(plugin_loader));
+		}
+	}
+
+	return g_strdup(_("Unknown"));
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+G_DEFINE_TYPE(GPluginGtkPluginPage, gplugin_gtk_plugin_page, GTK_TYPE_BOX)
+
+static void
+gplugin_gtk_plugin_page_set_property(
+	GObject *obj,
+	guint prop_id,
+	const GValue *value,
+	GParamSpec *pspec)
+{
+	GPluginGtkPluginPage *page = GPLUGIN_GTK_PLUGIN_PAGE(obj);
+
+	switch(prop_id) {
+		case PROP_PLUGIN:
+			gplugin_gtk_plugin_page_set_plugin(page, g_value_get_object(value));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gplugin_gtk_plugin_page_get_property(
+	GObject *obj,
+	guint prop_id,
+	GValue *value,
+	GParamSpec *pspec)
+{
+	GPluginGtkPluginPage *page = GPLUGIN_GTK_PLUGIN_PAGE(obj);
+
+	switch(prop_id) {
+		case PROP_PLUGIN:
+			g_value_set_object(value, gplugin_gtk_plugin_page_get_plugin(page));
+			break;
+		default:
+			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
+			break;
+	}
+}
+
+static void
+gplugin_gtk_plugin_page_dispose(GObject *obj)
+{
+	GPluginGtkPluginPage *page = GPLUGIN_GTK_PLUGIN_PAGE(obj);
+
+	g_clear_object(&page->plugin);
+
+	G_OBJECT_CLASS(gplugin_gtk_plugin_page_parent_class)->dispose(obj);
+}
+
+static void
+gplugin_gtk_plugin_page_init(GPluginGtkPluginPage *page)
+{
+	gtk_widget_init_template(GTK_WIDGET(page));
+}
+
+static void
+gplugin_gtk_plugin_page_class_init(GPluginGtkPluginPageClass *klass)
+{
+	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+	GObjectClass *obj_class = G_OBJECT_CLASS(klass);
+
+	obj_class->get_property = gplugin_gtk_plugin_page_get_property;
+	obj_class->set_property = gplugin_gtk_plugin_page_set_property;
+	obj_class->dispose = gplugin_gtk_plugin_page_dispose;
+
+	/* properties */
+
+	/**
+	 * GPluginGtkPluginPage:plugin:
+	 *
+	 * The [iface@GPlugin.Plugin] whose info should be displayed.
+	 *
+	 * Since: 0.39.0
+	 */
+	properties[PROP_PLUGIN] = g_param_spec_object(
+		"plugin",
+		"plugin",
+		"The GPluginPlugin whose info should be displayed",
+		G_TYPE_OBJECT,
+		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
+
+	/* signals */
+
+	/**
+	 * GPluginGtkPluginPage::plugin-state-set:
+	 * @page: The [class@GPluginGtk.PluginPage] instance.
+	 * @enabled: Whether the plugin was requested to be enabled or disabled.
+	 *
+	 * Emitted when the plugin page enable switch is toggled.
+	 *
+	 * Since: 0.39.0
+	 */
+	signals[SIG_PLUGIN_STATE_SET] = g_signal_new_class_handler(
+		"plugin-state-set",
+		G_OBJECT_CLASS_TYPE(klass),
+		G_SIGNAL_RUN_LAST,
+		NULL,
+		NULL,
+		NULL,
+		NULL,
+		G_TYPE_NONE,
+		1,
+		G_TYPE_BOOLEAN);
+
+	/* template stuff */
+	gtk_widget_class_set_template_from_resource(
+		widget_class,
+		"/org/imfreedom/keep/gplugin/gplugin-gtk/plugin-page.ui");
+
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_lookup_plugin_name);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_lookup_plugin_state);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_lookup_plugin_state_sensitivity);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_plugin_page_enable_state_set_cb);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_plugin_page_newline_strjoinv_cb);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_plugin_page_lookup_website_cb);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_plugin_page_lookup_error_cb);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_plugin_page_lookup_abi_version_cb);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_plugin_page_lookup_loader_cb);
+}
+
+/******************************************************************************
+ * Public API
+ *****************************************************************************/
+
+/**
+ * gplugin_gtk_plugin_page_new:
+ *
+ * Create a new [class@GPluginGtk4.PluginPage] which can be used to display
+ * info about a [iface@GPlugin.Plugin].
+ *
+ * Returns: (transfer full): The new widget.
+ *
+ * Since: 0.39.0
+ */
+GtkWidget *
+gplugin_gtk_plugin_page_new(void)
+{
+	return g_object_new(GPLUGIN_GTK_TYPE_PLUGIN_PAGE, NULL);
+}
+
+/**
+ * gplugin_gtk_plugin_page_set_plugin:
+ * @page: The plugin page instance.
+ * @plugin: (nullable): The plugin instance.
+ *
+ * Sets the [iface@GPlugin.Plugin] that should be displayed.
+ *
+ * A @plugin value of %NULL will clear the widget.
+ *
+ * Since: 0.39.0
+ */
+void
+gplugin_gtk_plugin_page_set_plugin(
+	GPluginGtkPluginPage *page,
+	GPluginPlugin *plugin)
+{
+	g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_PAGE(page));
+
+	if(g_set_object(&page->plugin, plugin)) {
+		g_object_notify_by_pspec(G_OBJECT(page), properties[PROP_PLUGIN]);
+	}
+}
+
+/**
+ * gplugin_gtk_plugin_page_get_plugin:
+ * @page: The plugin page instance.
+ *
+ * Gets the [iface@GPlugin.Plugin] that's being displayed.
+ *
+ * Returns: (transfer none) (nullable): The plugin that's being displayed, or
+ *          %NULL if the page is empty.
+ *
+ * Since: 0.39.0
+ */
+GPluginPlugin *
+gplugin_gtk_plugin_page_get_plugin(GPluginGtkPluginPage *page)
+{
+	g_return_val_if_fail(GPLUGIN_GTK_IS_PLUGIN_PAGE(page), NULL);
+
+	return page->plugin;
+}
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-row.h b/gplugin-gtk4/gplugin-gtk-plugin-row.h
--- a/gplugin-gtk4/gplugin-gtk-plugin-row.h
+++ b/gplugin-gtk4/gplugin-gtk-plugin-row.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ * Copyright (C) 2021-2022 Elliott Sales de Andrade <quantum.analyst@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -46,10 +46,6 @@ void gplugin_gtk_plugin_row_set_plugin(
 	GPluginGtkPluginRow *row,
 	GPluginPlugin *plugin);
 GPluginPlugin *gplugin_gtk_plugin_row_get_plugin(GPluginGtkPluginRow *row);
-gboolean gplugin_gtk_plugin_row_get_expanded(GPluginGtkPluginRow *row);
-void gplugin_gtk_plugin_row_set_expanded(
-	GPluginGtkPluginRow *row,
-	gboolean expanded);
 gchar *gplugin_gtk_plugin_row_get_sort_key(GPluginGtkPluginRow *row);
 gboolean gplugin_gtk_plugin_row_matches_search(
 	GPluginGtkPluginRow *row,
diff --git a/gplugin-gtk4/gplugin-gtk-plugin-row.c b/gplugin-gtk4/gplugin-gtk-plugin-row.c
--- a/gplugin-gtk4/gplugin-gtk-plugin-row.c
+++ b/gplugin-gtk4/gplugin-gtk-plugin-row.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ * Copyright (C) 2021-2022 Elliott Sales de Andrade <quantum.analyst@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -40,21 +40,6 @@ struct _GPluginGtkPluginRow {
 	/* Header */
 	GtkWidget *title;
 	GtkWidget *summary;
-	GtkWidget *config;
-	GtkWidget *revealer;
-
-	/* Details */
-	GtkWidget *description;
-	GtkWidget *authors_box;
-	GtkWidget *website;
-	GtkWidget *dependencies_box;
-	GtkWidget *error;
-	GtkWidget *id;
-	GtkWidget *filename;
-	GtkWidget *abi_version;
-	GtkWidget *loader;
-	GtkWidget *internal;
-	GtkWidget *load_on_query;
 };
 
 /******************************************************************************
@@ -63,7 +48,6 @@ struct _GPluginGtkPluginRow {
 enum {
 	PROP_ZERO,
 	PROP_PLUGIN,
-	PROP_EXPANDED,
 	N_PROPERTIES,
 };
 static GParamSpec *properties[N_PROPERTIES] = {
@@ -79,136 +63,6 @@ static guint signals[N_SIGNALS] = {
 };
 
 /******************************************************************************
- * Helpers
- *****************************************************************************/
-static void
-_gplugin_gtk_plugin_row_refresh(GPluginGtkPluginRow *row)
-{
-	GtkWidget *widget = NULL;
-	GError *error = NULL;
-	gchar *website = NULL;
-	gchar *description = NULL, *id = NULL, *abi_version = NULL;
-	gchar *loader = NULL;
-	gchar **authors = NULL;
-	gchar **dependencies = NULL;
-	guint32 abi_version_uint;
-	gboolean loq = FALSE, internal = FALSE;
-	const gchar *filename = NULL;
-
-	/* Remove all the children from the authors box. */
-	while((widget = gtk_widget_get_first_child(row->authors_box)) != NULL) {
-		gtk_box_remove(GTK_BOX(row->authors_box), widget);
-	}
-
-	/* Remove all the children from the dependencies box. */
-	while((widget = gtk_widget_get_first_child(row->dependencies_box)) !=
-		  NULL) {
-		gtk_box_remove(GTK_BOX(row->dependencies_box), widget);
-	}
-
-	/* Now get the info if we can. */
-	if(GPLUGIN_IS_PLUGIN(row->plugin)) {
-		GPluginPluginInfo *plugin_info = gplugin_plugin_get_info(row->plugin);
-		GPluginLoader *plugin_loader = gplugin_plugin_get_loader(row->plugin);
-
-		filename = gplugin_plugin_get_filename(row->plugin);
-		error = gplugin_plugin_get_error(row->plugin);
-
-		if(GPLUGIN_IS_LOADER(plugin_loader)) {
-			loader = g_strdup(G_OBJECT_TYPE_NAME(plugin_loader));
-		}
-
-		/* clang-format off */
-		g_object_get(
-			G_OBJECT(plugin_info),
-			"abi_version", &abi_version_uint,
-			"authors", &authors,
-			"description", &description,
-			"dependencies", &dependencies,
-			"id", &id,
-			"internal", &internal,
-			"load-on-query", &loq,
-			"website", &website,
-			NULL);
-		/* clang-format on */
-
-		/* Finagle the website. */
-		if(website) {
-			gchar *markup = g_markup_printf_escaped(
-				"<a href=\"%s\">%s</a>",
-				website,
-				website);
-			g_free(website);
-			website = markup;
-		}
-
-		/* Finagle the abi_version. */
-		abi_version = g_strdup_printf("%08x", abi_version_uint);
-
-		g_clear_object(&plugin_loader);
-		g_clear_object(&plugin_info);
-	}
-
-	gtk_label_set_markup(GTK_LABEL(row->website), website);
-	gtk_label_set_text(GTK_LABEL(row->description), description);
-	gtk_widget_set_visible(row->description, description != NULL);
-	gtk_label_set_text(GTK_LABEL(row->id), id);
-	gtk_label_set_text(
-		GTK_LABEL(row->error),
-		(error != NULL) ? error->message : "(none)");
-	gtk_label_set_text(GTK_LABEL(row->filename), filename);
-	gtk_label_set_text(GTK_LABEL(row->abi_version), abi_version);
-	gtk_label_set_text(
-		GTK_LABEL(row->loader),
-		(loader != NULL) ? loader : _("Unknown"));
-	gtk_label_set_text(GTK_LABEL(row->internal), internal ? _("Yes") : _("No"));
-	gtk_label_set_text(GTK_LABEL(row->load_on_query), loq ? _("Yes") : _("No"));
-	/* FIXME: Set this correctly when plugins get proper configuration. */
-	gtk_widget_set_sensitive(row->config, FALSE);
-
-	/* Set the authors. */
-	if(authors) {
-		gint i = 0;
-
-		for(i = 0; authors[i]; i++) {
-			widget = gtk_label_new(authors[i]);
-			gtk_widget_set_halign(widget, GTK_ALIGN_START);
-			gtk_widget_set_valign(widget, GTK_ALIGN_START);
-			gtk_box_append(GTK_BOX(row->authors_box), widget);
-		}
-	} else {
-		widget = gtk_label_new(_("(none)"));
-		gtk_box_append(GTK_BOX(row->authors_box), widget);
-	}
-
-	/* Set the dependencies. */
-	if(dependencies) {
-		gint i = 0;
-
-		for(i = 0; dependencies[i]; i++) {
-			widget = gtk_label_new(dependencies[i]);
-			gtk_widget_set_halign(widget, GTK_ALIGN_START);
-			gtk_widget_set_valign(widget, GTK_ALIGN_START);
-			gtk_box_append(GTK_BOX(row->dependencies_box), widget);
-		}
-	} else {
-		widget = gtk_label_new(_("(none)"));
-		gtk_box_append(GTK_BOX(row->dependencies_box), widget);
-	}
-
-	g_clear_error(&error);
-	g_free(loader);
-	g_free(abi_version);
-	g_strfreev(authors);
-	g_free(description);
-	g_strfreev(dependencies);
-	g_free(id);
-	g_free(website);
-
-	gtk_list_box_row_changed(GTK_LIST_BOX_ROW(row));
-}
-
-/******************************************************************************
  * Callbacks
  *****************************************************************************/
 static gboolean
@@ -245,11 +99,6 @@ gplugin_gtk_plugin_row_set_property(
 		case PROP_PLUGIN:
 			gplugin_gtk_plugin_row_set_plugin(row, g_value_get_object(value));
 			break;
-		case PROP_EXPANDED:
-			gplugin_gtk_plugin_row_set_expanded(
-				row,
-				g_value_get_boolean(value));
-			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
 			break;
@@ -269,11 +118,6 @@ gplugin_gtk_plugin_row_get_property(
 		case PROP_PLUGIN:
 			g_value_set_object(value, gplugin_gtk_plugin_row_get_plugin(row));
 			break;
-		case PROP_EXPANDED:
-			g_value_set_boolean(
-				value,
-				gplugin_gtk_plugin_row_get_expanded(row));
-			break;
 		default:
 			G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, pspec);
 			break;
@@ -322,13 +166,6 @@ gplugin_gtk_plugin_row_class_init(GPlugi
 		G_TYPE_OBJECT,
 		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
 
-	properties[PROP_EXPANDED] = g_param_spec_boolean(
-		"expanded",
-		"expanded",
-		"Whether the row has been opened to show details",
-		FALSE,
-		G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
-
 	g_object_class_install_properties(obj_class, N_PROPERTIES, properties);
 
 	/* signals */
@@ -368,14 +205,6 @@ gplugin_gtk_plugin_row_class_init(GPlugi
 		widget_class,
 		GPluginGtkPluginRow,
 		summary);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		config);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		revealer);
 	gtk_widget_class_bind_template_callback(
 		widget_class,
 		gplugin_gtk_plugin_row_enable_state_set_cb);
@@ -388,49 +217,6 @@ gplugin_gtk_plugin_row_class_init(GPlugi
 	gtk_widget_class_bind_template_callback(
 		widget_class,
 		gplugin_gtk_lookup_plugin_state_sensitivity);
-
-	/* Details */
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		description);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		authors_box);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		website);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		dependencies_box);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		error);
-	gtk_widget_class_bind_template_child(widget_class, GPluginGtkPluginRow, id);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		filename);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		abi_version);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		loader);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		internal);
-	gtk_widget_class_bind_template_child(
-		widget_class,
-		GPluginGtkPluginRow,
-		load_on_query);
 }
 
 /******************************************************************************
@@ -472,8 +258,6 @@ gplugin_gtk_plugin_row_set_plugin(
 	g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_ROW(row));
 
 	if(g_set_object(&row->plugin, plugin)) {
-		_gplugin_gtk_plugin_row_refresh(row);
-
 		g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_PLUGIN]);
 	}
 }
@@ -498,42 +282,6 @@ gplugin_gtk_plugin_row_get_plugin(GPlugi
 }
 
 /**
- * gplugin_gtk_plugin_row_get_expanded:
- * @row: The plugin row instance.
- *
- * Returns whether the plugin row is showing detailed information.
- *
- * Returns: %TRUE if the plugin row is expanded, %FALSE otherwise.
- *
- * Since: 0.38.0
- */
-gboolean
-gplugin_gtk_plugin_row_get_expanded(GPluginGtkPluginRow *row)
-{
-	g_return_val_if_fail(GPLUGIN_GTK_IS_PLUGIN_ROW(row), FALSE);
-
-	return gtk_revealer_get_reveal_child(GTK_REVEALER(row->revealer));
-}
-
-/**
- * gplugin_gtk_plugin_row_set_expanded:
- * @row: The plugin row instance.
- * @expanded: Whether the plugin row is expanded.
- *
- * Sets whether the plugin row is showing detailed information.
- *
- * Since: 0.38.0
- */
-void
-gplugin_gtk_plugin_row_set_expanded(GPluginGtkPluginRow *row, gboolean expanded)
-{
-	g_return_if_fail(GPLUGIN_GTK_IS_PLUGIN_ROW(row));
-
-	gtk_revealer_set_reveal_child(GTK_REVEALER(row->revealer), expanded);
-	g_object_notify_by_pspec(G_OBJECT(row), properties[PROP_EXPANDED]);
-}
-
-/**
  * gplugin_gtk_plugin_row_get_sort_key:
  * @row: The plugin row instance.
  *
@@ -581,14 +329,23 @@ gplugin_gtk_plugin_row_matches_search(
 		return TRUE;
 	}
 
-	value = gtk_label_get_text(GTK_LABEL(row->description));
-	if(g_strstr_len(value, -1, text)) {
-		return TRUE;
-	}
+	if(GPLUGIN_IS_PLUGIN(row->plugin)) {
+		GPluginPluginInfo *plugin_info = NULL;
+		gchar *filename = NULL;
 
-	value = gtk_label_get_text(GTK_LABEL(row->filename));
-	if(g_strstr_len(value, -1, text)) {
-		return TRUE;
+		plugin_info = gplugin_plugin_get_info(row->plugin);
+		value = gplugin_plugin_info_get_description(plugin_info);
+		g_clear_object(&plugin_info);
+		if(value != NULL && g_strstr_len(value, -1, text)) {
+			return TRUE;
+		}
+
+		filename = gplugin_plugin_get_filename(row->plugin);
+		if(filename != NULL && g_strstr_len(filename, -1, text)) {
+			g_free(filename);
+			return TRUE;
+		}
+		g_free(filename);
 	}
 
 	return FALSE;
diff --git a/gplugin-gtk4/gplugin-gtk-view.h b/gplugin-gtk4/gplugin-gtk-view.h
--- a/gplugin-gtk4/gplugin-gtk-view.h
+++ b/gplugin-gtk4/gplugin-gtk-view.h
@@ -52,6 +52,9 @@ void gplugin_gtk_view_set_show_internal(
 	gboolean show_internal);
 gboolean gplugin_gtk_view_get_show_internal(GPluginGtkView *view);
 
+void gplugin_gtk_view_show_overview(GPluginGtkView *view);
+void gplugin_gtk_view_show_plugin(GPluginGtkView *view, GPluginPlugin *plugin);
+
 G_END_DECLS
 
 #endif /* GPLUGIN_GTK_VIEW_H */
diff --git a/gplugin-gtk4/gplugin-gtk-view.c b/gplugin-gtk4/gplugin-gtk-view.c
--- a/gplugin-gtk4/gplugin-gtk-view.c
+++ b/gplugin-gtk4/gplugin-gtk-view.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2021 Elliott Sales de Andrade <quantum.analyst@gmail.com>
+ * Copyright (C) 2021-2022 Elliott Sales de Andrade <quantum.analyst@gmail.com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,14 +17,15 @@
 
 #include <gplugin.h>
 
+#include <gplugin-gtk-plugin-page.h>
 #include <gplugin-gtk-plugin-row.h>
 #include <gplugin-gtk-view.h>
 
 /**
  * GPluginGtkView:
  *
- * A [class@Gtk.ListBox] widget that displays all the plugins and some basic
- * information about them.
+ * A widget that displays all the plugins and some basic information about
+ * them.
  */
 
 /******************************************************************************
@@ -33,12 +34,17 @@
 struct _GPluginGtkView {
 	GtkBox parent;
 
+	GPluginManager *manager;
+	gboolean show_internal;
+	GtkWidget *stack;
+
+	/* Overview */
 	GtkWidget *list_box;
 	GtkWidget *search_bar;
 	GtkWidget *search_entry;
 
-	GPluginManager *manager;
-	gboolean show_internal;
+	/* Plugin details */
+	GtkWidget *plugin_page;
 };
 
 /******************************************************************************
@@ -61,25 +67,27 @@ static void
 gplugin_gtk_view_row_activated(
 	G_GNUC_UNUSED GtkListBox *box,
 	GtkListBoxRow *row,
-	G_GNUC_UNUSED gpointer data)
+	gpointer data)
 {
+	GPluginGtkView *view = GPLUGIN_GTK_VIEW(data);
 	GPluginGtkPluginRow *plugin_row = GPLUGIN_GTK_PLUGIN_ROW(row);
+	GPluginPlugin *plugin = NULL;
 
-	gplugin_gtk_plugin_row_set_expanded(
-		plugin_row,
-		!gplugin_gtk_plugin_row_get_expanded(plugin_row));
+	plugin = gplugin_gtk_plugin_row_get_plugin(plugin_row);
+	gplugin_gtk_view_show_plugin(view, plugin);
 }
 
 static void
 gplugin_gtk_view_plugin_state_set_cb(
-	GPluginGtkPluginRow *row,
+	GObject *row_or_page,
 	gboolean state,
 	gpointer data)
 {
 	GPluginGtkView *view = GPLUGIN_GTK_VIEW(data);
-	GPluginPlugin *plugin = gplugin_gtk_plugin_row_get_plugin(row);
+	GPluginPlugin *plugin = NULL;
 	GError *error = NULL;
 
+	g_object_get(row_or_page, "plugin", &plugin, NULL);
 	if(state) {
 		gplugin_manager_load_plugin(view->manager, plugin, &error);
 
@@ -97,6 +105,8 @@ gplugin_gtk_view_plugin_state_set_cb(
 			g_error_free(error);
 		}
 	}
+
+	g_clear_object(&plugin);
 }
 
 static void
@@ -173,6 +183,14 @@ gplugin_gtk_view_search_changed(
 	gtk_list_box_invalidate_filter(GTK_LIST_BOX(view->list_box));
 }
 
+static void
+gplugin_gtk_view_back_clicked_cb(G_GNUC_UNUSED GtkButton *button, gpointer data)
+{
+	GPluginGtkView *view = GPLUGIN_GTK_VIEW(data);
+
+	gplugin_gtk_view_show_overview(view);
+}
+
 /******************************************************************************
  * GObject Implementation
  *****************************************************************************/
@@ -250,6 +268,8 @@ gplugin_gtk_view_class_init(GPluginGtkVi
 		widget_class,
 		"/org/imfreedom/keep/gplugin/gplugin-gtk/view.ui");
 
+	gtk_widget_class_bind_template_child(widget_class, GPluginGtkView, stack);
+
 	gtk_widget_class_bind_template_child(
 		widget_class,
 		GPluginGtkView,
@@ -263,12 +283,23 @@ gplugin_gtk_view_class_init(GPluginGtkVi
 		GPluginGtkView,
 		search_entry);
 
+	gtk_widget_class_bind_template_child(
+		widget_class,
+		GPluginGtkView,
+		plugin_page);
+
 	gtk_widget_class_bind_template_callback(
 		widget_class,
 		gplugin_gtk_view_row_activated);
 	gtk_widget_class_bind_template_callback(
 		widget_class,
 		gplugin_gtk_view_search_changed);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_view_back_clicked_cb);
+	gtk_widget_class_bind_template_callback(
+		widget_class,
+		gplugin_gtk_view_plugin_state_set_cb);
 
 	/* properties */
 
@@ -418,3 +449,43 @@ gplugin_gtk_view_get_show_internal(GPlug
 
 	return view->show_internal;
 }
+
+/**
+ * gplugin_gtk_view_show_overview:
+ * @view: The GTK view instance.
+ *
+ * Shows the plugin list overview.
+ *
+ * Since: 0.39.0
+ */
+void
+gplugin_gtk_view_show_overview(GPluginGtkView *view)
+{
+	g_return_if_fail(GPLUGIN_GTK_IS_VIEW(view));
+
+	gplugin_gtk_plugin_page_set_plugin(
+		GPLUGIN_GTK_PLUGIN_PAGE(view->plugin_page),
+		NULL);
+	gtk_stack_set_visible_child_name(GTK_STACK(view->stack), "overview");
+}
+
+/**
+ * gplugin_gtk_view_show_plugin:
+ * @view: The GTK view instance.
+ * @plugin: (transfer none): The plugin to show.
+ *
+ * Shows a page for a single plugin.
+ *
+ * Since: 0.39.0
+ */
+void
+gplugin_gtk_view_show_plugin(GPluginGtkView *view, GPluginPlugin *plugin)
+{
+	g_return_if_fail(GPLUGIN_GTK_IS_VIEW(view));
+	g_return_if_fail(GPLUGIN_IS_PLUGIN(plugin));
+
+	gplugin_gtk_plugin_page_set_plugin(
+		GPLUGIN_GTK_PLUGIN_PAGE(view->plugin_page),
+		plugin);
+	gtk_stack_set_visible_child_name(GTK_STACK(view->stack), "plugin-page");
+}
diff --git a/gplugin-gtk4/meson.build b/gplugin-gtk4/meson.build
--- a/gplugin-gtk4/meson.build
+++ b/gplugin-gtk4/meson.build
@@ -9,11 +9,13 @@ if GTK4.found()
 GPLUGIN_GTK4_LIBRARY_VERSION = '0.1.0'
 
 GPLUGIN_GTK4_SOURCES = [
+	'gplugin-gtk-plugin-page.c',
 	'gplugin-gtk-plugin-row.c',
 	'gplugin-gtk-view.c',
 ]
 
 GPLUGIN_GTK4_HEADERS = [
+	'gplugin-gtk-plugin-page.h',
 	'gplugin-gtk-plugin-row.h',
 	'gplugin-gtk-view.h',
 ]
