use crate::protocol::{BatchEncoder, HostEvent, Mutation, ProtocolError};
use crate::schema::{AssetKind, MessageDuration, Selection, Theme, Window};
use std::task::{Context, Poll};
pub struct Batch {
encoder: BatchEncoder,
error: Option<ProtocolError>,
}
impl Default for Batch {
fn default() -> Self {
Self::new()
}
}
impl Batch {
pub fn new() -> Self {
Self {
encoder: BatchEncoder::with_capacity(16 * 1024, 4 * 1024, 256),
error: None,
}
}
pub fn arena(&self) -> (*const u8, usize) {
self.encoder.arena()
}
pub fn begin(&mut self) {
self.encoder.clear();
self.error = None;
}
pub fn finish(&mut self) -> Result<&[u8], ProtocolError> {
if let Some(error) = self.error.take() {
return Err(error);
}
self.encoder.finish()
}
pub fn write(&mut self, mutation: Mutation<'_>) {
if self.error.is_none() {
if let Err(error) = self.encoder.encode(&mutation) {
self.error = Some(error);
}
}
}
pub fn fail(&mut self, error: ProtocolError) {
self.error = Some(error);
}
pub fn set_theme(&mut self, theme: Theme) {
self.write(Mutation::SetTheme(theme));
if let Some(overlay) = theme.overlay {
self.write(Mutation::SetOverlayTokens(*overlay));
}
}
pub fn set_overlay_tokens(&mut self, tokens: crate::schema::OverlayTokens) {
self.write(Mutation::SetOverlayTokens(tokens));
}
pub fn set_window(&mut self, window: Window) {
self.write(Mutation::SetWindow(window));
}
pub fn set_text_node(&mut self, node_id: u32, text: &str, selection: Option<Selection>) {
self.write(Mutation::SetText {
node_id,
text,
selection,
});
}
pub fn register_asset(&mut self, asset_id: u32, kind: AssetKind, bytes: &[u8]) {
self.write(Mutation::RegisterAsset {
asset_id,
kind,
bytes,
});
}
pub fn release_asset(&mut self, asset_id: u32) {
self.write(Mutation::ReleaseAsset { asset_id });
}
pub fn show_message(
&mut self,
handler_id: u64,
text: &str,
action: &str,
duration: MessageDuration,
) {
self.write(Mutation::ShowMessage {
handler_id,
text,
action,
duration,
});
}
pub(crate) fn notification(&mut self, command: &crate::notification::NotificationCommand) {
use crate::notification::NotificationCommand;
match command {
NotificationCommand::Post(notification) => {
self.write(Mutation::PostNotification {
key: ¬ification.key,
title: ¬ification.title,
body: ¬ification.body,
channel: ¬ification.channel,
action_1: ¬ification.actions[0],
action_2: ¬ification.actions[1],
importance: notification.importance,
presentation: notification.presentation,
});
}
NotificationCommand::Withdraw(key) => {
self.write(Mutation::WithdrawNotification { key });
}
NotificationCommand::RequestPermission => {
self.write(Mutation::RequestNotificationPermission);
}
}
}
pub fn set_clipboard_text(&mut self, text: &str) {
self.write(Mutation::SetClipboardText { text });
}
pub fn read_clipboard(&mut self, request: u32) {
self.write(Mutation::ReadClipboard { request });
}
pub(crate) fn file_call(&mut self, call: &crate::picked_file::Call) {
self.write(Mutation::FileCall {
request: call.request,
op: call.op,
create: call.create,
handle: call.handle,
name: &call.name,
data: &call.data,
});
}
pub(crate) fn storage_call(&mut self, call: &crate::storage::RendererCall) {
self.write(Mutation::StorageCall {
request: call.request,
op: call.op,
store: &call.store,
key: &call.key,
data: &call.data,
});
}
pub(crate) fn release_file_handle(&mut self, handle: u32) {
self.write(Mutation::ReleaseFileHandle { handle });
}
pub fn scroll_to(&mut self, node_id: u32, request: crate::schema::ScrollTo) {
use crate::schema::scroll_to_flags as flag;
let mut flags = 0;
if request.animate {
flags |= flag::ANIMATE;
}
if request.x_px.is_some() {
flags |= flag::X;
}
if request.y_px.is_some() {
flags |= flag::Y;
}
let x_px = request.x_px.unwrap_or(0.0);
let y_px = request.y_px.unwrap_or(0.0);
if flags & (flag::X | flag::Y) == 0 || !x_px.is_finite() || !y_px.is_finite() {
self.fail(ProtocolError::InvalidValueKind(0));
return;
}
self.write(Mutation::ScrollTo {
node_id,
item: request.item,
flags,
x_px,
y_px,
});
}
pub fn scroll_into_view(&mut self, node_id: u32, request: crate::schema::ScrollIntoView) {
use crate::schema::{ScrollTarget, scroll_into_view_flags, scroll_target_kind};
let (target_kind, target, rect) = match request.target {
ScrollTarget::Content(rect) => (scroll_target_kind::CONTENT, 0, rect),
ScrollTarget::Node(node, rect) => (scroll_target_kind::NODE, node, rect),
ScrollTarget::Item(item, rect) => (scroll_target_kind::ITEM, item, rect),
};
let numbers = [rect.x_px, rect.y_px, rect.width_px, rect.height_px];
if numbers.iter().any(|value| !value.is_finite())
|| rect.width_px < 0.0
|| rect.height_px < 0.0
{
self.fail(ProtocolError::InvalidValueKind(0));
return;
}
self.write(Mutation::ScrollIntoView {
node_id,
target,
target_kind,
align_x: request.align_x as u16,
align_y: request.align_y as u16,
flags: if request.animate {
scroll_into_view_flags::ANIMATE
} else {
0
},
rect,
});
}
pub(crate) fn file_dialog(&mut self, request: &crate::file_dialog::Request) {
self.write(Mutation::ShowFileDialog {
request: request.id,
window: request.window,
kind: request.kind,
default_filter: request.default_filter,
title: &request.title,
directory: &request.directory,
name: &request.name,
filters: &request.filters,
});
}
pub fn edit_code(
&mut self,
node_id: u32,
request_id: u32,
base_version: u32,
range: crate::code::CodeRange,
text: &str,
) {
self.write(Mutation::EditCode {
node_id,
request_id,
base_version,
start_line: range.start.line,
start_column: range.start.column,
end_line: range.end.line,
end_column: range.end.column,
text,
});
}
pub fn set_selection(
&mut self,
node_id: u32,
base_version: u32,
anchor: crate::code::Position,
active: crate::code::Position,
) {
self.write(Mutation::SetSelection {
node_id,
base_version,
anchor_line: anchor.line,
anchor_column: anchor.column,
active_line: active.line,
active_column: active.column,
});
}
pub fn reveal_range(
&mut self,
node_id: u32,
base_version: u32,
range: crate::code::CodeRange,
align: crate::schema::RevealAlign,
) {
self.write(Mutation::RevealRange {
node_id,
base_version,
start_line: range.start.line,
start_column: range.start.column,
end_line: range.end.line,
end_column: range.end.column,
align,
});
}
pub fn set_folded(&mut self, node_id: u32, start_line: u32, collapsed: bool) {
self.write(Mutation::SetFolded {
node_id,
start_line,
collapsed,
});
}
pub fn set_scroll_offset(&mut self, node_id: u32, offset_px: f64) {
self.write(Mutation::SetScrollOffset { node_id, offset_px });
}
pub fn append_text_node(&mut self, node_id: u32, text: &str) {
self.write(Mutation::AppendText { node_id, text });
}
pub fn request_focus(&mut self, node_id: u32) {
self.write(Mutation::RequestFocus { node_id });
}
}
pub trait Runtime {
fn batch(&self) -> &Batch;
fn batch_mut(&mut self) -> &mut Batch;
fn rebuild(&mut self);
fn render(&mut self, frame_time_nanos: Option<u64>);
fn handle_event(&mut self, event: &HostEvent<'_>) -> Result<i64, ProtocolError>;
fn size_token(&self, node_id: u32) -> Option<u32> {
let _ = node_id;
None
}
fn is_window(&self, node_id: u32) -> bool {
let _ = node_id;
false
}
fn poll_work(&mut self, context: &mut Context<'_>) -> Poll<()> {
let _ = context;
Poll::Pending
}
fn run_in_context(&mut self, action: &mut dyn FnMut()) {
action();
}
}