diff --git a/Userland/DevTools/Profiler/CMakeLists.txt b/Userland/DevTools/Profiler/CMakeLists.txt index 539fdb7269..c5406b8480 100644 --- a/Userland/DevTools/Profiler/CMakeLists.txt +++ b/Userland/DevTools/Profiler/CMakeLists.txt @@ -1,15 +1,17 @@ set(SOURCES - DisassemblyModel.cpp - main.cpp - IndividualSampleModel.cpp - Process.cpp - ProcessPickerWidget.cpp - Profile.cpp - ProfileModel.cpp - TimelineTrack.cpp + DisassemblyModel.cpp + main.cpp + IndividualSampleModel.cpp + Process.cpp + ProcessPickerWidget.cpp + Profile.cpp + ProfileModel.cpp SamplesModel.cpp + TimelineContainer.cpp + TimelineHeader.cpp + TimelineTrack.cpp TimelineView.cpp -) + ) serenity_app(Profiler ICON app-profiler) target_link_libraries(Profiler LibGUI LibDesktop LibX86) diff --git a/Userland/DevTools/Profiler/TimelineContainer.cpp b/Userland/DevTools/Profiler/TimelineContainer.cpp new file mode 100644 index 0000000000..0810f30679 --- /dev/null +++ b/Userland/DevTools/Profiler/TimelineContainer.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "TimelineContainer.h" +#include "TimelineView.h" +#include + +namespace Profiler { + +TimelineContainer::TimelineContainer(GUI::Widget& header_container, TimelineView& timeline_view) +{ + m_header_container = header_container; + m_timeline_view = timeline_view; + add_child(header_container); + add_child(timeline_view); + header_container.move_to_back(); + timeline_view.move_to_back(); +} + +TimelineContainer::~TimelineContainer() +{ +} + +void TimelineContainer::did_scroll() +{ + AbstractScrollableWidget::did_scroll(); + update_widget_positions(); +} + +void TimelineContainer::update_widget_positions() +{ + m_header_container->move_to(0, -vertical_scrollbar().value()); + m_timeline_view->move_to(m_header_container->width() + -horizontal_scrollbar().value(), -vertical_scrollbar().value()); +} + +void TimelineContainer::update_widget_sizes() +{ + { + m_timeline_view->do_layout(); + auto preferred_size = m_timeline_view->layout()->preferred_size(); + m_timeline_view->resize(preferred_size); + set_content_size(preferred_size); + } + + { + m_header_container->do_layout(); + auto preferred_size = m_header_container->layout()->preferred_size(); + m_header_container->resize(preferred_size); + } +} + +void TimelineContainer::resize_event(GUI::ResizeEvent& event) +{ + AbstractScrollableWidget::resize_event(event); + update_widget_sizes(); +} + +} diff --git a/Userland/DevTools/Profiler/TimelineContainer.h b/Userland/DevTools/Profiler/TimelineContainer.h new file mode 100644 index 0000000000..9760e25ef3 --- /dev/null +++ b/Userland/DevTools/Profiler/TimelineContainer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Profiler { + +class TimelineView; + +class TimelineContainer : public GUI::AbstractScrollableWidget { + C_OBJECT(TimelineContainer); + +public: + virtual ~TimelineContainer(); + +protected: + virtual void did_scroll() override; + virtual void resize_event(GUI::ResizeEvent&) override; + +private: + void update_widget_sizes(); + void update_widget_positions(); + + TimelineContainer(GUI::Widget& header_container, TimelineView&); + + RefPtr m_timeline_view; + RefPtr m_header_container; +}; + +} diff --git a/Userland/DevTools/Profiler/TimelineHeader.cpp b/Userland/DevTools/Profiler/TimelineHeader.cpp new file mode 100644 index 0000000000..0e1b13a8cf --- /dev/null +++ b/Userland/DevTools/Profiler/TimelineHeader.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include "TimelineHeader.h" +#include "Process.h" +#include +#include +#include +#include + +namespace Profiler { + +TimelineHeader::TimelineHeader(Process const& process) + : m_process(process) +{ + set_frame_shape(Gfx::FrameShape::Panel); + set_frame_shadow(Gfx::FrameShadow::Raised); + set_fixed_size(200, 40); + + m_icon = GUI::FileIconProvider::icon_for_executable(m_process.executable).bitmap_for_size(32); + m_text = String::formatted("{} ({})", LexicalPath(m_process.executable).basename(), m_process.pid); +} + +TimelineHeader::~TimelineHeader() +{ +} + +void TimelineHeader::paint_event(GUI::PaintEvent& event) +{ + GUI::Frame::paint_event(event); + GUI::Painter painter(*this); + painter.add_clip_rect(event.rect()); + + Gfx::IntRect icon_rect { frame_thickness() + 2, 0, 32, 32 }; + icon_rect.center_vertically_within(frame_inner_rect()); + + if (m_icon) + painter.blit(icon_rect.location(), *m_icon, m_icon->rect()); + + Gfx::IntRect text_rect { + icon_rect.right() + 6, + icon_rect.y(), + width() - 32, + 32 + }; + text_rect.center_vertically_within(frame_inner_rect()); + + painter.draw_text(text_rect, m_text, Gfx::TextAlignment::CenterLeft); +} + +} diff --git a/Userland/DevTools/Profiler/TimelineHeader.h b/Userland/DevTools/Profiler/TimelineHeader.h new file mode 100644 index 0000000000..ba307aa7c0 --- /dev/null +++ b/Userland/DevTools/Profiler/TimelineHeader.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, Andreas Kling + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Profiler { + +class Process; + +class TimelineHeader final : public GUI::Frame { + C_OBJECT(TimelineHeader); + +public: + virtual ~TimelineHeader(); + +private: + TimelineHeader(Process const&); + + virtual void paint_event(GUI::PaintEvent&) override; + + Process const& m_process; + RefPtr m_icon; + String m_text; +}; + +} diff --git a/Userland/DevTools/Profiler/TimelineTrack.cpp b/Userland/DevTools/Profiler/TimelineTrack.cpp index 96d7b181ae..74f4d09e6f 100644 --- a/Userland/DevTools/Profiler/TimelineTrack.cpp +++ b/Userland/DevTools/Profiler/TimelineTrack.cpp @@ -74,15 +74,6 @@ void TimelineTrack::paint_event(GUI::PaintEvent& event) int select_hover_x = (int)((float)(normalized_hover_time - start_of_trace) * column_width); painter.fill_rect({ select_start_x, frame_thickness(), select_end_x - select_start_x, height() - frame_thickness() * 2 }, Color(0, 0, 0, 60)); painter.fill_rect({ select_hover_x, frame_thickness(), 1, height() - frame_thickness() * 2 }, Color::NamedColor::Black); - - auto text = String::formatted("{} ({})", m_process.executable, m_process.pid); - Gfx::IntRect text_rect { - frame_thickness() + 3, - frame_thickness() + 3, - font().width(text), - font().glyph_height() - }; - painter.draw_text(text_rect, text, font()); } u64 TimelineTrack::timestamp_at_x(int x) const diff --git a/Userland/DevTools/Profiler/main.cpp b/Userland/DevTools/Profiler/main.cpp index b908c9175b..a49640e1ab 100644 --- a/Userland/DevTools/Profiler/main.cpp +++ b/Userland/DevTools/Profiler/main.cpp @@ -7,6 +7,8 @@ #include "IndividualSampleModel.h" #include "ProcessPickerWidget.h" #include "Profile.h" +#include "TimelineContainer.h" +#include "TimelineHeader.h" #include "TimelineTrack.h" #include "TimelineView.h" #include @@ -25,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -91,6 +92,11 @@ int main(int argc, char** argv) main_widget.set_fill_with_background_color(true); main_widget.set_layout(); + auto timeline_header_container = GUI::Widget::construct(); + timeline_header_container->set_layout(); + timeline_header_container->set_fill_with_background_color(true); + timeline_header_container->set_shrink_to_fit(true); + auto timeline_view = TimelineView::construct(); for (auto& process : profile->processes()) { size_t event_count = 0; @@ -100,11 +106,11 @@ int main(int argc, char** argv) } if (!event_count) continue; + timeline_header_container->add(process); timeline_view->add(*timeline_view, *profile, process); } - auto& scrollable_container = main_widget.add(); - scrollable_container.set_widget(timeline_view.ptr()); + [[maybe_unused]] auto& timeline_container = main_widget.add(*timeline_header_container, *timeline_view); main_widget.add(*profile);