mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 05:37:34 +00:00
Ladybird: Add a table to display CSS properties to the AppKit Inspector
This commit is contained in:
parent
4483204c9c
commit
6304abf94c
1 changed files with 185 additions and 1 deletions
|
@ -5,6 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <AK/DeprecatedString.h>
|
#include <AK/DeprecatedString.h>
|
||||||
|
#include <LibWeb/CSS/Selector.h>
|
||||||
#include <LibWebView/ViewImplementation.h>
|
#include <LibWebView/ViewImplementation.h>
|
||||||
|
|
||||||
#import <UI/Inspector.h>
|
#import <UI/Inspector.h>
|
||||||
|
@ -20,13 +21,38 @@
|
||||||
static constexpr CGFloat const WINDOW_WIDTH = 600;
|
static constexpr CGFloat const WINDOW_WIDTH = 600;
|
||||||
static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
|
|
||||||
@interface Inspector () <NSOutlineViewDataSource, NSOutlineViewDelegate>
|
static NSString* const CSS_PROPERTY_COLUMN = @"Property";
|
||||||
|
static NSString* const CSS_VALUE_COLUMN = @"Value";
|
||||||
|
|
||||||
|
struct Selection {
|
||||||
|
bool operator==(Selection const& other) const = default;
|
||||||
|
|
||||||
|
i32 dom_node_id { 0 };
|
||||||
|
Optional<Web::CSS::Selector::PseudoElement> pseudo_element {};
|
||||||
|
};
|
||||||
|
|
||||||
|
@interface Inspector () <NSOutlineViewDataSource, NSOutlineViewDelegate, NSTableViewDataSource>
|
||||||
|
{
|
||||||
|
Selection m_selection;
|
||||||
|
}
|
||||||
|
|
||||||
@property (nonatomic, strong) Tab* tab;
|
@property (nonatomic, strong) Tab* tab;
|
||||||
|
|
||||||
@property (nonatomic, strong) NSOutlineView* dom_tree_outline_view;
|
@property (nonatomic, strong) NSOutlineView* dom_tree_outline_view;
|
||||||
@property (nonatomic, strong) NSDictionary* dom_tree;
|
@property (nonatomic, strong) NSDictionary* dom_tree;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSTableView* computed_style_table_view;
|
||||||
|
@property (nonatomic, strong) NSDictionary* computed_style;
|
||||||
|
@property (nonatomic, strong) NSArray* computed_style_keys;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSTableView* resolved_style_table_view;
|
||||||
|
@property (nonatomic, strong) NSDictionary* resolved_style;
|
||||||
|
@property (nonatomic, strong) NSArray* resolved_style_keys;
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSTableView* variables_table_view;
|
||||||
|
@property (nonatomic, strong) NSDictionary* variables;
|
||||||
|
@property (nonatomic, strong) NSArray* variables_keys;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation Inspector
|
@implementation Inspector
|
||||||
|
@ -56,7 +82,11 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
auto* top_tab_view = [[NSTabView alloc] init];
|
auto* top_tab_view = [[NSTabView alloc] init];
|
||||||
[split_view addSubview:top_tab_view];
|
[split_view addSubview:top_tab_view];
|
||||||
|
|
||||||
|
auto* bottom_tab_view = [[NSTabView alloc] init];
|
||||||
|
[split_view addSubview:bottom_tab_view];
|
||||||
|
|
||||||
[self initializeDOMTreeTab:top_tab_view];
|
[self initializeDOMTreeTab:top_tab_view];
|
||||||
|
[self initializeCSSTables:bottom_tab_view];
|
||||||
[self reset];
|
[self reset];
|
||||||
|
|
||||||
auto& web_view = [[self.tab web_view] view];
|
auto& web_view = [[self.tab web_view] view];
|
||||||
|
@ -93,6 +123,7 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
{
|
{
|
||||||
auto& web_view = [[self.tab web_view] view];
|
auto& web_view = [[self.tab web_view] view];
|
||||||
web_view.on_received_dom_tree = nullptr;
|
web_view.on_received_dom_tree = nullptr;
|
||||||
|
web_view.clear_inspected_dom_node();
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Public methods
|
#pragma mark - Public methods
|
||||||
|
@ -105,9 +136,23 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
|
|
||||||
- (void)reset
|
- (void)reset
|
||||||
{
|
{
|
||||||
|
m_selection = {};
|
||||||
|
|
||||||
self.dom_tree = @{};
|
self.dom_tree = @{};
|
||||||
[self.dom_tree_outline_view reloadItem:nil reloadChildren:YES];
|
[self.dom_tree_outline_view reloadItem:nil reloadChildren:YES];
|
||||||
[self.dom_tree_outline_view sizeToFit];
|
[self.dom_tree_outline_view sizeToFit];
|
||||||
|
|
||||||
|
self.computed_style = @{};
|
||||||
|
self.computed_style_keys = @[];
|
||||||
|
[self.computed_style_table_view reloadData];
|
||||||
|
|
||||||
|
self.resolved_style = @{};
|
||||||
|
self.resolved_style_keys = @[];
|
||||||
|
[self.resolved_style_table_view reloadData];
|
||||||
|
|
||||||
|
self.variables = @{};
|
||||||
|
self.variables_keys = @[];
|
||||||
|
[self.variables_table_view reloadData];
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Private methods
|
#pragma mark - Private methods
|
||||||
|
@ -136,6 +181,42 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
[tab_view addTabViewItem:tab];
|
[tab_view addTabViewItem:tab];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSTableView*)createCSSTable:(NSTabView*)tab_view identifier:(NSString*)identifier
|
||||||
|
{
|
||||||
|
auto* tab = [[NSTabViewItem alloc] initWithIdentifier:identifier];
|
||||||
|
[tab setLabel:identifier];
|
||||||
|
|
||||||
|
auto* scroll_view = [[NSScrollView alloc] init];
|
||||||
|
[scroll_view setHasVerticalScroller:YES];
|
||||||
|
[scroll_view setHasHorizontalScroller:YES];
|
||||||
|
[scroll_view setLineScroll:24];
|
||||||
|
[tab setView:scroll_view];
|
||||||
|
|
||||||
|
auto* table_view = [[NSTableView alloc] initWithFrame:[tab_view frame]];
|
||||||
|
[table_view setDataSource:self];
|
||||||
|
[table_view setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle];
|
||||||
|
[scroll_view setDocumentView:table_view];
|
||||||
|
|
||||||
|
auto* property_column = [[NSTableColumn alloc] initWithIdentifier:CSS_PROPERTY_COLUMN];
|
||||||
|
[property_column setTitle:CSS_PROPERTY_COLUMN];
|
||||||
|
[table_view addTableColumn:property_column];
|
||||||
|
|
||||||
|
auto* value_column = [[NSTableColumn alloc] initWithIdentifier:CSS_VALUE_COLUMN];
|
||||||
|
[value_column setTitle:CSS_VALUE_COLUMN];
|
||||||
|
[table_view addTableColumn:value_column];
|
||||||
|
|
||||||
|
[tab_view addTabViewItem:tab];
|
||||||
|
|
||||||
|
return table_view;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)initializeCSSTables:(NSTabView*)tab_view
|
||||||
|
{
|
||||||
|
self.computed_style_table_view = [self createCSSTable:tab_view identifier:@"Computed Style"];
|
||||||
|
self.resolved_style_table_view = [self createCSSTable:tab_view identifier:@"Resolved Style"];
|
||||||
|
self.variables_table_view = [self createCSSTable:tab_view identifier:@"Variables"];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)onTreeDoubleClick:(id)sender
|
- (void)onTreeDoubleClick:(id)sender
|
||||||
{
|
{
|
||||||
NSOutlineView* outline_view = sender;
|
NSOutlineView* outline_view = sender;
|
||||||
|
@ -148,6 +229,45 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)setSelection:(Selection)selection
|
||||||
|
{
|
||||||
|
if (selection == m_selection)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_selection = move(selection);
|
||||||
|
|
||||||
|
auto deserialize_json = [](auto const& json) {
|
||||||
|
auto* dictionary = Ladybird::deserialize_json_to_dictionary(json);
|
||||||
|
if (!dictionary) {
|
||||||
|
return @{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return dictionary;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto& web_view = [[self.tab web_view] view];
|
||||||
|
auto properties = web_view.inspect_dom_node(m_selection.dom_node_id, m_selection.pseudo_element);
|
||||||
|
|
||||||
|
if (!properties.is_error()) {
|
||||||
|
self.computed_style = deserialize_json(properties.value().computed_style_json);
|
||||||
|
self.resolved_style = deserialize_json(properties.value().resolved_style_json);
|
||||||
|
self.variables = deserialize_json(properties.value().custom_properties_json);
|
||||||
|
} else {
|
||||||
|
self.computed_style = @{};
|
||||||
|
self.resolved_style = @{};
|
||||||
|
self.variables = @{};
|
||||||
|
}
|
||||||
|
|
||||||
|
self.computed_style_keys = [[self.computed_style allKeys] sortedArrayUsingSelector:@selector(compare:)];
|
||||||
|
[self.computed_style_table_view reloadData];
|
||||||
|
|
||||||
|
self.resolved_style_keys = [[self.resolved_style allKeys] sortedArrayUsingSelector:@selector(compare:)];
|
||||||
|
[self.resolved_style_table_view reloadData];
|
||||||
|
|
||||||
|
self.variables_keys = [[self.variables allKeys] sortedArrayUsingSelector:@selector(compare:)];
|
||||||
|
[self.variables_table_view reloadData];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - NSOutlineViewDataSource
|
#pragma mark - NSOutlineViewDataSource
|
||||||
|
|
||||||
- (id)outlineView:(NSOutlineView*)view
|
- (id)outlineView:(NSOutlineView*)view
|
||||||
|
@ -254,4 +374,68 @@ static constexpr CGFloat const WINDOW_HEIGHT = 800;
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)outlineView:(NSOutlineView*)outline_view
|
||||||
|
shouldSelectItem:(id)item
|
||||||
|
{
|
||||||
|
i32 dom_node_id { 0 };
|
||||||
|
Optional<Web::CSS::Selector::PseudoElement> pseudo_element;
|
||||||
|
|
||||||
|
if (id element = [item objectForKey:@"pseudo-element"]) {
|
||||||
|
dom_node_id = [[item objectForKey:@"parent-id"] intValue];
|
||||||
|
pseudo_element = static_cast<Web::CSS::Selector::PseudoElement>([element intValue]);
|
||||||
|
} else {
|
||||||
|
dom_node_id = [[item objectForKey:@"id"] intValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self setSelection: { dom_node_id, move(pseudo_element) }];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - NSTableViewDataSource
|
||||||
|
|
||||||
|
- (NSInteger)numberOfRowsInTableView:(NSTableView*)table_view
|
||||||
|
{
|
||||||
|
if (table_view == self.computed_style_table_view) {
|
||||||
|
return static_cast<NSInteger>(self.computed_style.count);
|
||||||
|
}
|
||||||
|
if (table_view == self.resolved_style_table_view) {
|
||||||
|
return static_cast<NSInteger>(self.resolved_style.count);
|
||||||
|
}
|
||||||
|
if (table_view == self.variables_table_view) {
|
||||||
|
return static_cast<NSInteger>(self.variables.count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id)tableView:(NSTableView*)table_view
|
||||||
|
objectValueForTableColumn:(NSTableColumn*)table_column
|
||||||
|
row:(NSInteger)row
|
||||||
|
{
|
||||||
|
NSDictionary* values = nil;
|
||||||
|
NSArray* keys = nil;
|
||||||
|
|
||||||
|
if (table_view == self.computed_style_table_view) {
|
||||||
|
values = self.computed_style;
|
||||||
|
keys = self.computed_style_keys;
|
||||||
|
} else if (table_view == self.resolved_style_table_view) {
|
||||||
|
values = self.resolved_style;
|
||||||
|
keys = self.resolved_style_keys;
|
||||||
|
} else if (table_view == self.variables_table_view) {
|
||||||
|
values = self.variables;
|
||||||
|
keys = self.variables_keys;
|
||||||
|
} else {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ([[table_column identifier] isEqualToString:CSS_PROPERTY_COLUMN]) {
|
||||||
|
return keys[row];
|
||||||
|
}
|
||||||
|
if ([[table_column identifier] isEqualToString:CSS_VALUE_COLUMN]) {
|
||||||
|
return values[keys[row]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue