Skip to content

Commit

Permalink
Show details subtree
Browse files Browse the repository at this point in the history
  • Loading branch information
itome committed Apr 28, 2024
1 parent 944ce14 commit 2d534e3
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 17 deletions.
48 changes: 31 additions & 17 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use crate::components::sdk_version::SdkVersionComponent;
use crate::components::select_device_popup::SelectDevicePopupComponent;
use crate::components::select_launch_configuration_popup::SelectLaunchConfigurationPopupComponent;
use crate::components::select_tab_handler::SelectTabControllerComponent;
use crate::components::widget_details::WidgetDetailsComponent;
use crate::redux::action::Action;
use crate::redux::selector::current_session::current_session_selector;
use crate::redux::state::{
Expand Down Expand Up @@ -72,6 +73,7 @@ pub enum ComponentId {
App,
Performance,
Inspector,
WidgetDetails,
LaunchConfigurations,
SdkVersion,
}
Expand Down Expand Up @@ -161,6 +163,10 @@ impl App {
ComponentId::Inspector,
Box::new(InspectorComponent::new()) as Box<dyn Component>,
),
(
ComponentId::WidgetDetails,
Box::new(WidgetDetailsComponent::new()) as Box<dyn Component>,
),
(
ComponentId::NetworkRequest,
Box::new(NetworkRequestComponent::new()) as Box<dyn Component>,
Expand Down Expand Up @@ -399,7 +405,8 @@ impl App {
let tab_layout = Layout::default()
.direction(Direction::Vertical)
.constraints(match state.focus {
Focus::DevTools(DevTools::Inspector) => vec![
Focus::DevTools(DevTools::Inspector)
| Focus::DevTools(DevTools::WidgetDetails) => vec![
Constraint::Length(3),
Constraint::Fill(1),
Constraint::Length(2),
Expand Down Expand Up @@ -436,22 +443,29 @@ impl App {
self.component(&ComponentId::Network)
.draw(f, tab_layout[3], state);

if state.focus == Focus::DevTools(DevTools::Performance) {
let vertical_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)])
.split(layout[1]);
self.component(&ComponentId::Frames)
.draw(f, vertical_layout[0], state);
self.component(&ComponentId::FrameAnalysis)
.draw(f, vertical_layout[1], state)
} else if state.focus == Focus::DevTools(DevTools::App) {
self.component(&ComponentId::Logs).draw(f, layout[1], state);
} else if state.focus == Focus::DevTools(DevTools::Network)
|| state.focus == Focus::DevTools(DevTools::NetworkRequest)
{
self.component(&ComponentId::NetworkRequest)
.draw(f, layout[1], state);
match state.focus {
Focus::DevTools(DevTools::Performance) => {
let vertical_layout = Layout::default()
.direction(Direction::Vertical)
.constraints([Constraint::Percentage(30), Constraint::Percentage(70)])
.split(layout[1]);
self.component(&ComponentId::Frames)
.draw(f, vertical_layout[0], state);
self.component(&ComponentId::FrameAnalysis)
.draw(f, vertical_layout[1], state)
}
Focus::DevTools(DevTools::App) => {
self.component(&ComponentId::Logs).draw(f, layout[1], state);
}
Focus::DevTools(DevTools::Network) | Focus::DevTools(DevTools::NetworkRequest) => {
self.component(&ComponentId::NetworkRequest)
.draw(f, layout[1], state);
}
Focus::DevTools(DevTools::Inspector) | Focus::DevTools(DevTools::WidgetDetails) => {
self.component(&ComponentId::WidgetDetails)
.draw(f, layout[1], state);
}
_ => {}
}
})?;
Ok(())
Expand Down
1 change: 1 addition & 0 deletions src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub mod sdk_version;
pub mod select_device_popup;
pub mod select_launch_configuration_popup;
pub mod select_tab_handler;
pub mod widget_details;

/// `Component` is a trait that represents a visual and interactive element of the user interface.
/// Implementors of this trait can be registered with the main application loop and will be able to receive events,
Expand Down
82 changes: 82 additions & 0 deletions src/redux/reducer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1071,5 +1071,87 @@ pub fn reducer(state: State, action: Action) -> State {
.collect(),
..state
},
Action::SetSelectedWidgetDetailsTree { session_id, tree } => State {
sessions: state
.sessions
.into_iter()
.map(|s| {
if s.id == session_id {
SessionState {
selected_widget_details_tree: tree.clone(),
..s
}
} else {
s
}
})
.collect(),
..state
},
Action::SetSelectedWidgetObjectGroup { session_id, group } => State {
sessions: state
.sessions
.into_iter()
.map(|s| {
if s.id == session_id {
SessionState {
selected_widget_object_group: group.clone(),
..s
}
} else {
s
}
})
.collect(),
..state
},
Action::SetOpenWidgetDetailsValueId { session_id, ids } => State {
sessions: state
.sessions
.into_iter()
.map(|s| {
if s.id == session_id {
SessionState {
opened_widget_details_value_ids: ids.clone(),
..s
}
} else {
s
}
})
.collect(),
..state
},
Action::ToggleOpenWidgetDetailsValueId { session_id, id } => State {
sessions: state
.sessions
.into_iter()
.map(|s| {
if s.id == session_id {
let mut opened_widget_value_ids = s.opened_widget_details_value_ids.clone();
if opened_widget_value_ids.contains(&id) {
opened_widget_value_ids.remove(&id);
} else {
opened_widget_value_ids.insert(id.clone());
}
SessionState {
opened_widget_details_value_ids: opened_widget_value_ids,
..s
}
} else {
s
}
})
.collect(),
..state
},
Action::EnterWidgetDetails => State {
focus: Focus::DevTools(DevTools::WidgetDetails),
..state
},
Action::ExitWidgetDetails => State {
focus: Focus::DevTools(DevTools::Inspector),
..state
},
}
}
4 changes: 4 additions & 0 deletions src/redux/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub enum DevTools {
App,
Performance,
Inspector,
WidgetDetails,
Network,
NetworkRequest,
}
Expand Down Expand Up @@ -111,6 +112,9 @@ pub struct SessionState {
pub widget_summary_tree: Option<DiagnosticNode>,
pub selected_widget_value_id: Option<String>,
pub opened_widget_value_ids: HashSet<String>,
pub selected_widget_object_group: Option<String>,
pub selected_widget_details_tree: Option<DiagnosticNode>,
pub opened_widget_details_value_ids: HashSet<String>,
}

#[derive(Default, Clone, PartialEq, Eq)]
Expand Down
5 changes: 5 additions & 0 deletions src/redux/thunk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod context;
pub mod hot_reload;
pub mod hot_restart;
pub mod launch_emulator;
pub mod load_details_subtree;
pub mod load_emulators;
pub mod load_full_request;
pub mod load_root_widget_summary_tree;
Expand All @@ -32,6 +33,7 @@ pub enum ThunkAction {
LoadFullRequest,
LoadVSCodeLaunchSetting,
LoadRootWidgetSummaryTree { session_id: String },
LoadDetailsSubtree { value_id: String },
RunNewApp { use_fvm: bool },
LaunchEmulator,
HotReload,
Expand Down Expand Up @@ -73,5 +75,8 @@ where
ThunkAction::LoadSupportedPlatforms => Box::new(
load_supported_platforms::LoadSupportedPlatformsThunk::new(context),
),
ThunkAction::LoadDetailsSubtree { value_id } => Box::new(
load_details_subtree::LoadDetailsSubtreeThunk::new(context, value_id),
),
}
}
148 changes: 148 additions & 0 deletions src/redux/thunk/load_details_subtree.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
use async_trait::async_trait;
use color_eyre::eyre::Result;
use std::{
collections::{HashMap, HashSet},
sync::Arc,
time::Duration,
};
use tokio::time::sleep;

use redux_rs::{middlewares::thunk::Thunk, StoreApi};

use crate::redux::{
action::Action,
selector::current_session::current_session_selector_cloned,
state::{SessionState, State},
};

use devtools::{
protocols::{
flutter_extension::{DiagnosticNode, FlutterExtensionProtocol},
io_extension::IoExtensionProtocol,
vm_service::{EventKind, StreamId, VmServiceProtocol},
},
vm_service::VmService,
};

use daemon::flutter::FlutterDaemon;

use super::context::Context;

pub struct LoadDetailsSubtreeThunk {
context: Arc<Context>,
value_id: String,
}

impl LoadDetailsSubtreeThunk {
pub fn new(context: Arc<Context>, value_id: String) -> Self {
Self { context, value_id }
}

fn all_ids(node: &DiagnosticNode, depth: usize) -> HashSet<String> {
let mut ids = HashSet::new();
if depth == 0 {
return ids;
}
ids.insert(node.value_id.clone().unwrap_or_default());
if let Some(children) = node.children.as_ref() {
for child in children {
ids.extend(Self::all_ids(child, depth - 1));
}
}
if let Some(properties) = node.properties.as_ref() {
for property in properties {
ids.extend(Self::all_ids(property, depth - 1));
}
}
ids
}
}

#[async_trait]
impl<Api> Thunk<State, Action, Api> for LoadDetailsSubtreeThunk
where
Api: StoreApi<State, Action> + Send + Sync + 'static,
{
async fn execute(&self, store: Arc<Api>) {
let Some(SessionState {
id: session_id,
selected_widget_object_group: prev_object_group,
..
}) = store.select(current_session_selector_cloned).await
else {
return;
};

let next_object_group = format!("subtree-{}-{}", session_id, self.value_id);

store
.dispatch(Action::SetSelectedWidgetObjectGroup {
session_id: session_id.clone(),
group: Some(next_object_group.clone()),
})
.await;

store
.dispatch(Action::SetSelectedWidgetDetailsTree {
session_id: session_id.clone(),
tree: None,
})
.await;

let Ok(session) = self
.context
.session_manager
.session(session_id.clone())
.await
else {
return;
};

let session = session.read().await;
let vm_service = &session.as_ref().unwrap().vm_service;

let Ok(vm) = vm_service.get_vm().await else {
return;
};
let Some(main_isolate) = vm.isolates.iter().find(|isolate| isolate.name == "main") else {
return;
};

if let Err(e) = vm_service
.dispose_group(&main_isolate.id, &next_object_group)
.await
{
log::error!("Failed to dispose group: {:?}", e);
};

let response = match vm_service
.get_details_subtree(
&main_isolate.id,
Some(&self.value_id),
Some(2),
&next_object_group,
)
.await
{
Ok(response) => response,
Err(err) => {
log::error!("Failed to get details subtree: {:?}", err);
return;
}
};

store
.dispatch(Action::SetOpenWidgetDetailsValueId {
session_id: session_id.clone(),
ids: Self::all_ids(&response.result, 5),
})
.await;

store
.dispatch(Action::SetSelectedWidgetDetailsTree {
session_id: session_id.clone(),
tree: Some(response.result),
})
.await;
}
}

0 comments on commit 2d534e3

Please sign in to comment.