use futures::channel::oneshot::{Sender, channel};
use perspective_js::utils::*;
use wasm_bindgen::prelude::*;
use yew::prelude::*;
use super::PerspectiveViewer;
use super::msg::PerspectiveViewerMsg::*;
use crate::components::font_loader::FontLoaderStatus;
use crate::components::settings_panel::SelectedTab;
use crate::config::*;
use crate::presentation::{ColumnSettingsTab, ColumnSettingsTarget, OpenColumnSettings};
use crate::queries::get_current_column_locator;
use crate::tasks::*;
#[derive(Default)]
pub(super) struct SettingsGeometry {
pub pane_width_override: Option<i32>,
pub selected_tab: SelectedTab,
pub auto_width: f64,
pub divider_target: Option<i32>,
pub divider_pumping: bool,
pub open_deltas: Option<(f64, f64)>,
pub column_settings_width_override: Option<i32>,
pub column_settings_auto_width: f64,
pub column_settings_pinned: bool,
pub column_settings_target: Option<OpenColumnSettings>,
pub column_settings_commit_pending: bool,
pub column_settings_docked_width: Option<f64>,
}
impl PerspectiveViewer {
pub(super) fn on_toggle_settings_init(
&mut self,
ctx: &Context<Self>,
update: Option<SettingsUpdate>,
announce: bool,
resolve: Option<Sender<ApiResult<JsValue>>>,
) -> bool {
match (update, resolve) {
(Some(SettingsUpdate::Missing), None) => false,
(Some(SettingsUpdate::Missing), Some(resolve)) => {
resolve.send(Ok(JsValue::UNDEFINED)).unwrap();
false
},
(Some(SettingsUpdate::SetDefault), resolve) => {
self.init_toggle_settings_task(ctx, Some(false), announce, resolve);
false
},
(Some(SettingsUpdate::Update(force)), resolve) => {
self.init_toggle_settings_task(ctx, Some(force), announce, resolve);
false
},
(None, resolve) => {
self.init_toggle_settings_task(ctx, None, announce, resolve);
false
},
}
}
pub(super) fn on_toggle_settings_complete(
&mut self,
ctx: &Context<Self>,
update: SettingsUpdate,
resolve: Sender<()>,
) -> bool {
match update {
SettingsUpdate::SetDefault if self.settings_open => {
ctx.props().presentation.set_open_column_settings(None);
self.settings_open = false;
self.on_rendered.push(resolve);
true
},
SettingsUpdate::Update(force) if force != self.settings_open => {
ctx.props().presentation.set_open_column_settings(None);
self.settings_open = force;
self.on_rendered.push(resolve);
true
},
_ if matches!(self.fonts.get_status(), FontLoaderStatus::Finished) => {
if let Err(e) = resolve.send(()) {
tracing::error!("toggle settings failed {:?}", e);
}
false
},
_ => {
ctx.props().presentation.set_open_column_settings(None);
self.on_rendered.push(resolve);
true
},
}
}
fn init_toggle_settings_task(
&mut self,
ctx: &Context<Self>,
force: Option<bool>,
announce: bool,
sender: Option<Sender<ApiResult<JsValue>>>,
) {
let is_open = ctx.props().presentation.is_settings_open();
match force {
Some(force) if is_open == force => {
if let Some(sender) = sender {
sender.send(Ok(JsValue::UNDEFINED)).unwrap();
}
},
Some(_) | None => {
ctx.props().presentation.set_settings_before_open(!is_open);
let force = !is_open;
let callback = ctx.link().callback(move |resolve| {
let update = SettingsUpdate::Update(force);
ToggleSettingsComplete(update, resolve)
});
if is_open {
self.settings_geometry.open_deltas =
measure_settings_open_deltas(&ctx.props().elem);
}
let open_deltas = self.settings_geometry.open_deltas;
let workspace = ctx.props().workspace.clone();
let presentation = ctx.props().presentation.clone();
let elem = ctx.props().elem.clone();
ApiFuture::spawn(async move {
let result: ApiResult<JsValue> = async {
if is_open {
let presents = presize_visible_panels_grown(&workspace, &elem).await;
let (notify, rendered) = channel::<()>();
callback.emit(notify);
presentation.set_settings_open(false, announce);
rendered.await?;
presents.reveal();
resize_visible_panels(&workspace).await;
} else {
let presents = if let Some((delta_w, delta_h)) = open_deltas {
presize_visible_panels_open(&workspace, &elem, delta_w, delta_h)
.await
} else {
StagedPresents::default()
};
let (notify, rendered) = channel::<()>();
callback.emit(notify);
presentation.set_settings_open(true, announce);
rendered.await?;
presents.reveal();
resize_visible_panels(&workspace).await;
}
Ok(JsValue::UNDEFINED)
}
.await;
if let Some(sender) = sender {
let msg = result.ignore_view_delete();
sender
.send(msg.map(|x| x.unwrap_or(JsValue::UNDEFINED)))
.into_apierror()?;
};
Ok(JsValue::undefined())
});
},
};
}
pub(super) fn on_settings_panel_size_update(&mut self, x: Option<i32>) -> bool {
match x {
Some(x) => {
self.settings_geometry.pane_width_override = Some(x);
false
},
None => {
self.settings_geometry.pane_width_override = None;
self.settings_geometry.auto_width = 0.0;
self.on_settings_panel_dimensions_reset.emit(());
true
},
}
}
pub(super) fn on_settings_divider_move(
&mut self,
ctx: &Context<Self>,
pane_width: i32,
) -> bool {
self.settings_geometry.divider_target = Some(pane_width);
if !self.settings_geometry.divider_pumping {
self.settings_geometry.divider_pumping = true;
ctx.link().send_message(SettingsDividerPump);
}
false
}
pub(super) fn on_settings_divider_pump(&mut self, ctx: &Context<Self>) -> bool {
if let Some(pane_width) = self.settings_geometry.divider_target.take() {
let workspace = ctx.props().workspace.clone();
let elem = ctx.props().elem.clone();
let link = ctx.link().clone();
ApiFuture::spawn(async move {
let presents =
presize_visible_panels_pane_width(&workspace, &elem, pane_width as f64).await;
link.send_message(SettingsDividerCommit(pane_width));
presents.reveal();
Ok(())
});
} else {
self.settings_geometry.divider_pumping = false;
}
false
}
pub(super) fn on_settings_divider_commit(
&mut self,
ctx: &Context<Self>,
pane_width: i32,
) -> bool {
self.settings_geometry.pane_width_override = Some(pane_width);
ctx.link().send_message(SettingsDividerPump);
true
}
pub(super) fn on_settings_divider_finish(&mut self, ctx: &Context<Self>) -> bool {
let workspace = ctx.props().workspace.clone();
ApiFuture::spawn(async move {
resize_visible_panels(&workspace).await;
Ok(())
});
false
}
pub(super) fn on_settings_panel_tab_changed(&mut self, tab: SelectedTab) -> bool {
let changed = tab != self.settings_geometry.selected_tab;
self.settings_geometry.selected_tab = tab;
changed
}
pub(super) fn on_settings_panel_auto_width(&mut self, w: f64) -> bool {
if w > self.settings_geometry.auto_width {
self.settings_geometry.auto_width = w;
true
} else {
false
}
}
pub(super) fn on_open_column_settings(
&mut self,
ctx: &Context<Self>,
target: Option<ColumnSettingsTarget>,
sender: Option<Sender<()>>,
toggle: bool,
) -> bool {
let mut open_column_settings = ctx.props().presentation.get_open_column_settings();
if target == open_column_settings.target {
if toggle {
ctx.props().presentation.set_open_column_settings(None);
}
} else {
open_column_settings.target.clone_from(&target);
open_column_settings.tab = match &target {
Some(ColumnSettingsTarget::NewExpression) => Some(ColumnSettingsTab::Attributes),
Some(ColumnSettingsTarget::Column(name)) => {
Some(if self.session_props.is_column_active(name) {
ColumnSettingsTab::Style
} else {
ColumnSettingsTab::Attributes
})
},
None => None,
};
ctx.props()
.presentation
.set_open_column_settings(Some(open_column_settings));
if target.is_some() {
self.settings_geometry.selected_tab = SelectedTab::Query;
}
}
if let Some(sender) = sender {
self.on_rendered.push(sender);
}
true
}
pub(super) fn on_column_settings_panel_size_update(&mut self, x: Option<i32>) -> bool {
self.settings_geometry.column_settings_width_override = x;
if x.is_none() {
self.settings_geometry.column_settings_auto_width = 0.0;
true
} else {
false
}
}
pub(super) fn on_column_settings_panel_auto_width(&mut self, w: f64) -> bool {
if w > self.settings_geometry.column_settings_auto_width {
self.settings_geometry.column_settings_auto_width = w;
true
} else {
false
}
}
pub(super) fn on_toggle_column_settings_pin(&mut self, ctx: &Context<Self>) -> bool {
let is_pinned = self.settings_geometry.column_settings_pinned;
let delta_w = measure_column_settings_pin_delta(&ctx.props().elem, is_pinned);
if let Some(delta_w) = delta_w {
self.settings_geometry.column_settings_docked_width = Some(delta_w.abs());
}
self.presize_column_settings_shift(ctx, delta_w, ToggleColumnSettingsPinComplete);
false
}
pub(super) fn on_toggle_column_settings_pin_complete(&mut self, resolve: Sender<()>) -> bool {
self.settings_geometry.column_settings_pinned =
!self.settings_geometry.column_settings_pinned;
self.on_rendered.push(resolve);
true
}
fn presize_column_settings_shift(
&self,
ctx: &Context<Self>,
delta_w: Option<f64>,
commit_msg: fn(Sender<()>) -> super::msg::PerspectiveViewerMsg,
) {
let workspace = ctx.props().workspace.clone();
let elem = ctx.props().elem.clone();
let callback = ctx.link().callback(commit_msg);
ApiFuture::spawn(async move {
let presents = match delta_w {
Some(delta_w) => presize_visible_panels_open(&workspace, &elem, delta_w, 0.0).await,
None => StagedPresents::default(),
};
let (notify, rendered) = channel::<()>();
callback.emit(notify);
rendered.await?;
presents.reveal();
resize_visible_panels(&workspace).await;
Ok(())
});
}
pub(super) fn is_column_settings_mounted(&self, ocs: &OpenColumnSettings) -> bool {
get_current_column_locator(
ocs,
&self.active_renderer,
&self.session_props.config,
&self.session_props.metadata,
)
.is_some()
}
pub(super) fn on_update_column_settings(
&mut self,
ctx: &Context<Self>,
ocs: OpenColumnSettings,
) -> bool {
self.refresh_session_snapshot(ctx);
let effective = self
.settings_geometry
.column_settings_target
.as_ref()
.unwrap_or(&self.presentation_props.open_column_settings);
if ocs == *effective {
return false;
}
let was_mounted =
self.is_column_settings_mounted(&self.presentation_props.open_column_settings);
let will_mount = self.is_column_settings_mounted(&ocs);
let needs_presize =
self.settings_geometry.column_settings_pinned && was_mounted != will_mount;
if !needs_presize && !self.settings_geometry.column_settings_commit_pending {
self.presentation_props.open_column_settings = ocs;
return true;
}
self.settings_geometry.column_settings_target = Some(ocs);
if self.settings_geometry.column_settings_commit_pending {
return false;
}
self.settings_geometry.column_settings_commit_pending = true;
let delta_w = if !needs_presize {
None
} else if was_mounted {
let delta = measure_column_settings_pin_delta(&ctx.props().elem, true);
if let Some(delta) = delta {
self.settings_geometry.column_settings_docked_width = Some(delta.abs());
}
delta
} else {
self.settings_geometry.column_settings_docked_width
};
self.presize_column_settings_shift(ctx, delta_w, UpdateColumnSettingsCommit);
false
}
pub(super) fn on_update_column_settings_commit(&mut self, resolve: Sender<()>) -> bool {
self.settings_geometry.column_settings_commit_pending = false;
self.on_rendered.push(resolve);
if let Some(ocs) = self.settings_geometry.column_settings_target.take() {
self.presentation_props.open_column_settings = ocs;
}
true
}
pub(super) fn on_column_settings_tab_changed(
&mut self,
ctx: &Context<Self>,
tab: ColumnSettingsTab,
) -> bool {
let mut open_column_settings = ctx.props().presentation.get_open_column_settings();
open_column_settings.tab.clone_from(&Some(tab));
ctx.props()
.presentation
.set_open_column_settings(Some(open_column_settings));
true
}
pub(super) fn on_toggle_debug(&mut self, _ctx: &Context<Self>) -> bool {
self.debug_open = !self.debug_open;
true
}
}