diff options
author | Andreas Kling <awesomekling@gmail.com> | 2019-08-19 20:29:52 +0200 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-08-19 20:29:52 +0200 |
commit | 4f3234148a3e994db1532671a911b97c996e590a (patch) | |
tree | bd54b8bfa0dd2936265cc998e02b173773c23136 | |
parent | 736dc5f6c061483c62817d48e1ba57bdd79e2ede (diff) | |
download | serenity-4f3234148a3e994db1532671a911b97c996e590a.zip |
Inspector: Show remote object properties in a table view
This patch expands the object model of this program quite a bit.
We now have a RemoteProcess object that contains a list of remote root
RemoteObject objects.
The RemoteProcess vends a RemoteObjectGraphModel&, and indices in that
model have internal_data() pointing to a corresponding RemoteObject.
RemoteObjects in turn vend a RemoteObjectPropertyModel&, which is what
we use to show the object properties.
This is pretty cool :^)
-rw-r--r-- | DevTools/Inspector/Makefile | 3 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObject.cpp | 13 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObject.h | 27 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObjectGraphModel.cpp | 67 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObjectGraphModel.h | 27 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObjectPropertyModel.cpp | 46 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteObjectPropertyModel.h | 37 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteProcess.cpp | 66 | ||||
-rw-r--r-- | DevTools/Inspector/RemoteProcess.h | 22 | ||||
-rw-r--r-- | DevTools/Inspector/main.cpp | 24 |
10 files changed, 254 insertions, 78 deletions
diff --git a/DevTools/Inspector/Makefile b/DevTools/Inspector/Makefile index a60cc4fbde..8c62b3864d 100644 --- a/DevTools/Inspector/Makefile +++ b/DevTools/Inspector/Makefile @@ -2,6 +2,9 @@ include ../../Makefile.common OBJS = \ RemoteObjectGraphModel.o \ + RemoteObjectPropertyModel.o \ + RemoteProcess.o \ + RemoteObject.o \ main.o APP = Inspector diff --git a/DevTools/Inspector/RemoteObject.cpp b/DevTools/Inspector/RemoteObject.cpp new file mode 100644 index 0000000000..e927f8d2b0 --- /dev/null +++ b/DevTools/Inspector/RemoteObject.cpp @@ -0,0 +1,13 @@ +#include "RemoteObject.h" +#include "RemoteObjectPropertyModel.h" + +RemoteObject::RemoteObject() + : m_property_model(RemoteObjectPropertyModel::create(*this)) +{ +} + +RemoteObjectPropertyModel& RemoteObject::property_model() +{ + m_property_model->update(); + return *m_property_model; +} diff --git a/DevTools/Inspector/RemoteObject.h b/DevTools/Inspector/RemoteObject.h new file mode 100644 index 0000000000..1718683276 --- /dev/null +++ b/DevTools/Inspector/RemoteObject.h @@ -0,0 +1,27 @@ +#pragma once + +#include <AK/AKString.h> +#include <AK/JsonObject.h> +#include <AK/NonnullOwnPtrVector.h> +#include <AK/Vector.h> + +class RemoteObjectPropertyModel; + +class RemoteObject { +public: + RemoteObject(); + + RemoteObjectPropertyModel& property_model(); + + RemoteObject* parent { nullptr }; + NonnullOwnPtrVector<RemoteObject> children; + + String address; + String parent_address; + String class_name; + String name; + + JsonObject json; + + NonnullRefPtr<RemoteObjectPropertyModel> m_property_model; +}; diff --git a/DevTools/Inspector/RemoteObjectGraphModel.cpp b/DevTools/Inspector/RemoteObjectGraphModel.cpp index 749a59e78b..2c9a0c1910 100644 --- a/DevTools/Inspector/RemoteObjectGraphModel.cpp +++ b/DevTools/Inspector/RemoteObjectGraphModel.cpp @@ -1,12 +1,14 @@ #include "RemoteObjectGraphModel.h" +#include "RemoteObject.h" +#include "RemoteProcess.h" #include <AK/JsonObject.h> #include <AK/JsonValue.h> #include <LibDraw/PNGLoader.h> #include <LibGUI/GApplication.h> #include <stdio.h> -RemoteObjectGraphModel::RemoteObjectGraphModel(pid_t pid) - : m_pid(pid) +RemoteObjectGraphModel::RemoteObjectGraphModel(RemoteProcess& process) + : m_process(process) { m_object_icon.set_bitmap_for_size(16, load_png("/res/icons/16x16/inspector-object.png")); m_window_icon.set_bitmap_for_size(16, load_png("/res/icons/16x16/window.png")); @@ -19,12 +21,12 @@ RemoteObjectGraphModel::~RemoteObjectGraphModel() GModelIndex RemoteObjectGraphModel::index(int row, int column, const GModelIndex& parent) const { if (!parent.is_valid()) { - if (m_remote_roots.is_empty()) + if (m_process.roots().is_empty()) return {}; - return create_index(row, column, &m_remote_roots.at(row)); + return create_index(row, column, &m_process.roots().at(row)); } auto& remote_parent = *static_cast<RemoteObject*>(parent.internal_data()); - return create_index(row, column, remote_parent.children.at(row)); + return create_index(row, column, &remote_parent.children.at(row)); } GModelIndex RemoteObjectGraphModel::parent_index(const GModelIndex& index) const @@ -35,7 +37,7 @@ GModelIndex RemoteObjectGraphModel::parent_index(const GModelIndex& index) const if (!remote_object.parent) return {}; for (int row = 0; row < remote_object.parent->children.size(); ++row) { - if (remote_object.parent->children[row] == &remote_object) + if (&remote_object.parent->children[row] == &remote_object) return create_index(row, 0, remote_object.parent); } @@ -46,7 +48,7 @@ GModelIndex RemoteObjectGraphModel::parent_index(const GModelIndex& index) const int RemoteObjectGraphModel::row_count(const GModelIndex& index) const { if (!index.is_valid()) - return m_remote_roots.size(); + return m_process.roots().size(); auto& remote_object = *static_cast<RemoteObject*>(index.internal_data()); return remote_object.children.size(); } @@ -72,54 +74,5 @@ GVariant RemoteObjectGraphModel::data(const GModelIndex& index, Role role) const void RemoteObjectGraphModel::update() { - auto success = m_socket.connect(CSocketAddress::local(String::format("/tmp/rpc.%d", m_pid))); - if (!success) { - fprintf(stderr, "Couldn't connect to PID %d\n", m_pid); - exit(1); - } - - m_socket.on_connected = [this] { - dbg() << "Connected to PID " << m_pid; - }; - - m_socket.on_ready_to_read = [this] { - if (m_socket.eof()) { - dbg() << "Disconnected from PID " << m_pid; - m_socket.close(); - return; - } - - auto data = m_socket.read_all(); - auto json_value = JsonValue::from_string(data); - ASSERT(json_value.is_array()); - m_json = json_value.as_array(); - - Vector<NonnullOwnPtr<RemoteObject>> remote_objects; - HashMap<String, RemoteObject*> objects_by_address; - - for (auto& value : m_json.values()) { - ASSERT(value.is_object()); - auto& object = value.as_object(); - auto remote_object = make<RemoteObject>(); - remote_object->address = object.get("address").to_string(); - remote_object->parent_address = object.get("parent").to_string(); - remote_object->name = object.get("name").to_string(); - remote_object->class_name = object.get("class_name").to_string(); - objects_by_address.set(remote_object->address, remote_object); - remote_objects.append(move(remote_object)); - } - - for (int i = 0; i < remote_objects.size(); ++i) { - auto& remote_object = remote_objects[i]; - auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr); - if (!parent) { - m_remote_roots.append(move(remote_object)); - } else { - remote_object->parent = parent; - parent->children.append(move(remote_object)); - } - } - - did_update(); - }; + did_update(); } diff --git a/DevTools/Inspector/RemoteObjectGraphModel.h b/DevTools/Inspector/RemoteObjectGraphModel.h index 0edbd2981a..5dc64dbbdf 100644 --- a/DevTools/Inspector/RemoteObjectGraphModel.h +++ b/DevTools/Inspector/RemoteObjectGraphModel.h @@ -1,15 +1,18 @@ #pragma once #include <AK/JsonArray.h> +#include <AK/JsonObject.h> #include <AK/NonnullOwnPtrVector.h> #include <LibCore/CLocalSocket.h> #include <LibGUI/GModel.h> +class RemoteProcess; + class RemoteObjectGraphModel final : public GModel { public: - static NonnullRefPtr<RemoteObjectGraphModel> create_with_pid(pid_t pid) + static NonnullRefPtr<RemoteObjectGraphModel> create(RemoteProcess& process) { - return adopt(*new RemoteObjectGraphModel(pid)); + return adopt(*new RemoteObjectGraphModel(process)); } virtual ~RemoteObjectGraphModel() override; @@ -22,22 +25,10 @@ public: virtual void update() override; private: - struct RemoteObject { - RemoteObject* parent { nullptr }; - Vector<OwnPtr<RemoteObject>> children; - - String address; - String parent_address; - String class_name; - String name; - }; - - explicit RemoteObjectGraphModel(pid_t); - - pid_t m_pid { -1 }; - CLocalSocket m_socket; - JsonArray m_json; - NonnullOwnPtrVector<RemoteObject> m_remote_roots; + explicit RemoteObjectGraphModel(RemoteProcess&); + + RemoteProcess& m_process; + GIcon m_object_icon; GIcon m_window_icon; }; diff --git a/DevTools/Inspector/RemoteObjectPropertyModel.cpp b/DevTools/Inspector/RemoteObjectPropertyModel.cpp new file mode 100644 index 0000000000..3e9ad0e9d2 --- /dev/null +++ b/DevTools/Inspector/RemoteObjectPropertyModel.cpp @@ -0,0 +1,46 @@ +#include "RemoteObjectPropertyModel.h" +#include "RemoteObject.h" + +RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object) + : m_object(object) +{ +} + +int RemoteObjectPropertyModel::row_count(const GModelIndex&) const +{ + return m_properties.size(); +} + +String RemoteObjectPropertyModel::column_name(int column) const +{ + switch (column) { + case Column::Name: + return "Name"; + case Column::Value: + return "Value"; + } + ASSERT_NOT_REACHED(); +} + +GVariant RemoteObjectPropertyModel::data(const GModelIndex& index, Role role) const +{ + auto& property = m_properties[index.row()]; + if (role == Role::Display) { + switch (index.column()) { + case Column::Name: + return property.name; + case Column::Value: + return property.value; + } + } + return {}; +} + +void RemoteObjectPropertyModel::update() +{ + m_properties.clear(); + m_object.json.for_each_member([this](auto& name, auto& value) { + m_properties.append({ name, value }); + }); + did_update(); +} diff --git a/DevTools/Inspector/RemoteObjectPropertyModel.h b/DevTools/Inspector/RemoteObjectPropertyModel.h new file mode 100644 index 0000000000..9ff905ecd1 --- /dev/null +++ b/DevTools/Inspector/RemoteObjectPropertyModel.h @@ -0,0 +1,37 @@ +#pragma once + +#include <AK/JsonValue.h> +#include <LibGUI/GModel.h> + +class RemoteObject; + +class RemoteObjectPropertyModel final : public GModel { +public: + virtual ~RemoteObjectPropertyModel() override {} + static NonnullRefPtr<RemoteObjectPropertyModel> create(RemoteObject& object) + { + return adopt(*new RemoteObjectPropertyModel(object)); + } + + enum Column { + Name, + Value, + __Count, + }; + + virtual int row_count(const GModelIndex& = GModelIndex()) const override; + virtual int column_count(const GModelIndex& = GModelIndex()) const override { return Column::__Count; } + virtual String column_name(int) const override; + virtual GVariant data(const GModelIndex&, Role = Role::Display) const override; + virtual void update() override; + +private: + explicit RemoteObjectPropertyModel(RemoteObject&); + + RemoteObject& m_object; + struct NameAndValue { + JsonValue name; + JsonValue value; + }; + Vector<NameAndValue> m_properties; +}; diff --git a/DevTools/Inspector/RemoteProcess.cpp b/DevTools/Inspector/RemoteProcess.cpp new file mode 100644 index 0000000000..50a21abc77 --- /dev/null +++ b/DevTools/Inspector/RemoteProcess.cpp @@ -0,0 +1,66 @@ +#include "RemoteProcess.h" +#include "RemoteObject.h" +#include "RemoteObjectGraphModel.h" +#include "RemoteObjectPropertyModel.h" +#include <stdio.h> + +RemoteProcess::RemoteProcess(pid_t pid) + : m_pid(pid) + , m_object_graph_model(RemoteObjectGraphModel::create(*this)) +{ +} + +void RemoteProcess::update() +{ + auto success = m_socket.connect(CSocketAddress::local(String::format("/tmp/rpc.%d", m_pid))); + if (!success) { + fprintf(stderr, "Couldn't connect to PID %d\n", m_pid); + exit(1); + } + + m_socket.on_connected = [this] { + dbg() << "Connected to PID " << m_pid; + }; + + m_socket.on_ready_to_read = [this] { + if (m_socket.eof()) { + dbg() << "Disconnected from PID " << m_pid; + m_socket.close(); + return; + } + + auto data = m_socket.read_all(); + auto json_value = JsonValue::from_string(data); + ASSERT(json_value.is_array()); + auto& object_array = json_value.as_array(); + + Vector<NonnullOwnPtr<RemoteObject>> remote_objects; + HashMap<String, RemoteObject*> objects_by_address; + + for (auto& value : object_array.values()) { + ASSERT(value.is_object()); + auto& object = value.as_object(); + auto remote_object = make<RemoteObject>(); + remote_object->address = object.get("address").to_string(); + remote_object->parent_address = object.get("parent").to_string(); + remote_object->name = object.get("name").to_string(); + remote_object->class_name = object.get("class_name").to_string(); + remote_object->json = object; + objects_by_address.set(remote_object->address, remote_object); + remote_objects.append(move(remote_object)); + } + + for (int i = 0; i < remote_objects.size(); ++i) { + auto& remote_object = remote_objects[i]; + auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr); + if (!parent) { + m_roots.append(move(remote_object)); + } else { + remote_object->parent = parent; + parent->children.append(move(remote_object)); + } + } + + m_object_graph_model->update(); + }; +} diff --git a/DevTools/Inspector/RemoteProcess.h b/DevTools/Inspector/RemoteProcess.h new file mode 100644 index 0000000000..8b37242955 --- /dev/null +++ b/DevTools/Inspector/RemoteProcess.h @@ -0,0 +1,22 @@ +#pragma once + +#include <AK/NonnullOwnPtrVector.h> +#include <LibCore/CLocalSocket.h> + +class RemoteObjectGraphModel; +class RemoteObject; + +class RemoteProcess { +public: + explicit RemoteProcess(pid_t); + void update(); + + RemoteObjectGraphModel& object_graph_model() { return *m_object_graph_model; } + const NonnullOwnPtrVector<RemoteObject>& roots() const { return m_roots; } + +private: + pid_t m_pid { -1 }; + NonnullRefPtr<RemoteObjectGraphModel> m_object_graph_model; + CLocalSocket m_socket; + NonnullOwnPtrVector<RemoteObject> m_roots; +}; diff --git a/DevTools/Inspector/main.cpp b/DevTools/Inspector/main.cpp index b0d1dd8ee6..0e57e39951 100644 --- a/DevTools/Inspector/main.cpp +++ b/DevTools/Inspector/main.cpp @@ -1,6 +1,11 @@ +#include "RemoteObject.h" #include "RemoteObjectGraphModel.h" +#include "RemoteObjectPropertyModel.h" +#include "RemoteProcess.h" #include <LibGUI/GApplication.h> #include <LibGUI/GBoxLayout.h> +#include <LibGUI/GSplitter.h> +#include <LibGUI/GTableView.h> #include <LibGUI/GTreeView.h> #include <LibGUI/GWindow.h> #include <stdio.h> @@ -32,10 +37,23 @@ int main(int argc, char** argv) widget->set_fill_with_background_color(true); widget->set_layout(make<GBoxLayout>(Orientation::Vertical)); - auto* tree_view = new GTreeView(widget); - tree_view->set_model(RemoteObjectGraphModel::create_with_pid(pid)); - tree_view->model()->update(); + auto* splitter = new GSplitter(Orientation::Horizontal, widget); + + RemoteProcess remote_process(pid); + + auto* tree_view = new GTreeView(splitter); + tree_view->set_model(remote_process.object_graph_model()); + tree_view->set_activates_on_selection(true); + + auto* properties_table_view = new GTableView(splitter); + properties_table_view->set_size_columns_to_fit_content(true); + + tree_view->on_activation = [&](auto& index) { + auto* remote_object = static_cast<RemoteObject*>(index.internal_data()); + properties_table_view->set_model(remote_object->property_model()); + }; window->show(); + remote_process.update(); return app.exec(); } |