Split WASM and native versions, and move iced support code to native
This commit is contained in:
597
src/bin/native/hex_view.rs
Normal file
597
src/bin/native/hex_view.rs
Normal file
@@ -0,0 +1,597 @@
|
||||
use std::{
|
||||
fmt::{self, Display}, ops::Deref
|
||||
};
|
||||
|
||||
use iced::{
|
||||
advanced::{layout::{Limits, Node}, overlay, renderer::{Quad, Style}, text::Renderer, widget::{tree::{State, Tag}, Operation, Tree}, Clipboard, Layout, Shell, Text, Widget}, alignment::Vertical, keyboard::{key::Named, Key}, mouse::{Button, Cursor, Interaction, ScrollDelta}, widget::{column, lazy, text}, Color, Element, Event, Fill, Font, Length, Padding, Pixels, Point, Rectangle, Size, Task, Vector
|
||||
};
|
||||
// use iced_core::{
|
||||
// Clipboard, Color, Event, Layout, Length, Pixels, Point, Rectangle, Shell, Size, Text, Vector,
|
||||
// Widget,
|
||||
// layout::{Limits, Node},
|
||||
// mouse::{Cursor, Interaction},
|
||||
// overlay,
|
||||
// renderer::{Quad, Style},
|
||||
// widget::{
|
||||
// Operation, Tree,
|
||||
// tree::{State, Tag},
|
||||
// },
|
||||
// };
|
||||
use nes_emu::Memory;
|
||||
|
||||
use nes_emu::{PPU, Mapped, PPUMMRegisters};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Cpu<'a>(&'a Mapped);
|
||||
|
||||
impl Memory for Cpu<'_> {
|
||||
fn peek(&self, val: usize) -> Option<u8> {
|
||||
self.0.peek_cpu(val as u16)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
0x10000
|
||||
}
|
||||
|
||||
fn edit_ver(&self) -> usize {
|
||||
self.0.cpu_edit_ver()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Ppu<'a>(&'a Mapped, &'a PPU);
|
||||
|
||||
impl Memory for Ppu<'_> {
|
||||
fn peek(&self, val: usize) -> Option<u8> {
|
||||
match self.0.peek_ppu(val as u16) {
|
||||
Ok(v) => Some(v),
|
||||
Err(Some((PPUMMRegisters::Palette, off))) => Some(self.1.palette.ram(off as u8)),
|
||||
Err(None) => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
0x4000
|
||||
}
|
||||
|
||||
fn edit_ver(&self) -> usize {
|
||||
self.0.ppu_edit_ver()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Oam<'a>(pub &'a PPU);
|
||||
|
||||
impl Memory for Oam<'_> {
|
||||
fn peek(&self, val: usize) -> Option<u8> {
|
||||
Some(self.0.peek_oam(val as u8))
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
0x100
|
||||
}
|
||||
|
||||
fn edit_ver(&self) -> usize {
|
||||
self.0.oam_edit_ver()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HexEvent {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct HexView {}
|
||||
|
||||
struct Val(Option<u8>);
|
||||
impl fmt::Display for Val {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if let Some(val) = self.0 {
|
||||
write!(f, "{:02X}", val)
|
||||
} else {
|
||||
write!(f, "XX")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HexView {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
|
||||
pub fn render_any<'a, M: Memory + Copy + 'a>(&self, mem: M) -> Element<'a, HexEvent> {
|
||||
struct Row<M: Memory>(usize, M);
|
||||
impl<'a, M: Memory + 'a> fmt::Display for Row<M> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", Val(self.1.peek(self.0)))?;
|
||||
for i in 1..16 {
|
||||
write!(f, " {}", Val(self.1.peek(self.0 + i)))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
column![
|
||||
text!("Hex view"),
|
||||
iced::widget::scrollable(lazy(mem.edit_ver(), move |_| column(
|
||||
[
|
||||
text!(" | 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F")
|
||||
.font(Font::MONOSPACE)
|
||||
.into()
|
||||
]
|
||||
.into_iter()
|
||||
.chain((0..mem.len()).step_by(16).map(|off| {
|
||||
text!(" {off:04X} | {}", Row(off, mem))
|
||||
.font(Font::MONOSPACE)
|
||||
.into()
|
||||
}))
|
||||
)))
|
||||
.width(Fill),
|
||||
]
|
||||
.width(Fill)
|
||||
.into()
|
||||
}
|
||||
pub fn render_cpu<'a>(&self, mem: &'a Mapped) -> Element<'a, HexEvent> {
|
||||
// self.render_any(Cpu(mem))
|
||||
Element::new(
|
||||
hex_editor::<Cpu<'a>, HexEvent, iced::Renderer>(Cpu(mem)).font(Font::MONOSPACE),
|
||||
)
|
||||
}
|
||||
pub fn render_ppu<'a>(&self, mem: &'a Mapped, ppu: &'a PPU) -> Element<'a, HexEvent> {
|
||||
// self.render_any(Ppu(mem, ppu))
|
||||
Element::new(
|
||||
hex_editor::<Ppu<'a>, HexEvent, iced::Renderer>(Ppu(mem, ppu)).font(Font::MONOSPACE),
|
||||
)
|
||||
}
|
||||
pub fn render_oam<'a>(&self, ppu: &'a PPU) -> Element<'a, HexEvent> {
|
||||
Element::new(
|
||||
hex_editor::<Oam<'a>, HexEvent, iced::Renderer>(Oam(ppu)).font(Font::MONOSPACE),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn update(&mut self, ev: HexEvent) -> Task<HexEvent> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BufferSlice<'a>(pub &'a [u8]);
|
||||
|
||||
impl Deref for BufferSlice<'_> {
|
||||
type Target = [u8];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Buffer {
|
||||
fn peek(&self, val: usize) -> Option<u8>;
|
||||
fn len(&self) -> usize;
|
||||
}
|
||||
|
||||
impl Buffer for BufferSlice<'_> {
|
||||
fn peek(&self, val: usize) -> Option<u8> {
|
||||
self.get(val).copied()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.iter().len()
|
||||
}
|
||||
}
|
||||
impl<M: Memory> Buffer for M {
|
||||
fn peek(&self, val: usize) -> Option<u8> {
|
||||
self.peek(val)
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hex_editor<B: Buffer, M, R: Renderer>(raw: B) -> HexEditor<B, M, R> {
|
||||
HexEditor {
|
||||
val: raw,
|
||||
on_edit: None,
|
||||
font: None,
|
||||
font_size: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct HexEdit {
|
||||
position: usize,
|
||||
old_value: u8,
|
||||
new_value: u8,
|
||||
}
|
||||
|
||||
pub struct HexEditor<B, M, R: Renderer> {
|
||||
val: B,
|
||||
font_size: Option<Pixels>,
|
||||
font: Option<R::Font>,
|
||||
on_edit: Option<Box<dyn Fn(HexEdit) -> M>>,
|
||||
}
|
||||
|
||||
impl<B, M, R> HexEditor<B, M, R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
pub fn font(mut self, font: R::Font) -> Self {
|
||||
self.font = Some(font);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buffer, M, R> HexEditor<B, M, R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn value_bounds(&self, renderer: &R, layout: Layout<'_>) -> Rectangle {
|
||||
let size = self.font_size.unwrap_or(renderer.default_size());
|
||||
layout
|
||||
.bounds()
|
||||
.shrink(Padding::new(0.).top(size.0 * 1.5).right(10.))
|
||||
}
|
||||
|
||||
fn scrollbar_bounds(&self, renderer: &R, layout: Layout<'_>) -> Rectangle {
|
||||
let size = self.font_size.unwrap_or(renderer.default_size());
|
||||
layout.bounds().shrink(
|
||||
Padding::new(0.)
|
||||
.top(size.0 * 1.5)
|
||||
.left(layout.bounds().width - 10.),
|
||||
)
|
||||
}
|
||||
|
||||
fn row_height(&self, renderer: &R) -> f32 {
|
||||
self.font_size.unwrap_or(renderer.default_size()).0 * LINE_HEIGHT
|
||||
}
|
||||
|
||||
fn scroll_max(&self, renderer: &R, layout: Layout<'_>) -> f32 {
|
||||
let size = self.font_size.unwrap_or(renderer.default_size());
|
||||
let bounds = layout.bounds().shrink(Padding::new(0.).top(size.0 * 1.5));
|
||||
let rows = self.val.len().div_ceil(0x10);
|
||||
(self.row_height(renderer) * rows as f32 - bounds.height).max(0.)
|
||||
}
|
||||
|
||||
fn scroll(
|
||||
&self,
|
||||
state: &mut HexEditorState,
|
||||
renderer: &R,
|
||||
layout: Layout<'_>,
|
||||
delta: &ScrollDelta,
|
||||
) {
|
||||
// let size = self.font_size.unwrap_or(renderer.default_size());
|
||||
// let bounds = layout.bounds().shrink(Padding::new(0.).top(size.0 * 1.5));
|
||||
// let rows = self.val.len().div_ceil(0x10);
|
||||
let max = self.scroll_max(renderer, layout);
|
||||
// println!("max: {max}, rows: {rows}");
|
||||
match delta {
|
||||
ScrollDelta::Lines { y, .. } => {
|
||||
state.offset_y += y * self.row_height(renderer);
|
||||
}
|
||||
ScrollDelta::Pixels { y, .. } => {
|
||||
state.offset_y += y;
|
||||
}
|
||||
}
|
||||
if state.offset_y > 0. {
|
||||
state.offset_y = 0.;
|
||||
} else if state.offset_y < -max {
|
||||
state.offset_y = -max;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct HexEditorState {
|
||||
offset_y: f32,
|
||||
selected: usize,
|
||||
dragging: bool,
|
||||
}
|
||||
|
||||
const LINE_HEIGHT: f32 = 1.3;
|
||||
|
||||
impl<B: Buffer, M, T, R> Widget<M, T, R> for HexEditor<B, M, R>
|
||||
where
|
||||
R: Renderer,
|
||||
{
|
||||
fn tag(&self) -> Tag {
|
||||
Tag::of::<HexEditorState>()
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
State::new(HexEditorState::default())
|
||||
}
|
||||
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size::new(Length::Fill, Length::Fill)
|
||||
}
|
||||
|
||||
fn layout(&mut self, _tree: &mut Tree, _renderer: &R, limits: &Limits) -> Node {
|
||||
Node::new(limits.max())
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut R,
|
||||
theme: &T,
|
||||
style: &Style,
|
||||
layout: Layout<'_>,
|
||||
cursor: Cursor,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
let state: &HexEditorState = tree.state.downcast_ref();
|
||||
let size = self.font_size.unwrap_or(renderer.default_size());
|
||||
let font = self.font.unwrap_or(renderer.default_font());
|
||||
|
||||
// let fonts = font_system();
|
||||
// let mut font_sys = fonts.write().unwrap();
|
||||
// let id = font_sys.raw().db().query(&Query {
|
||||
// families: &[Family::Monospace],
|
||||
// ..Query::default()
|
||||
// });
|
||||
// let f = font_sys.raw().get_font(id.unwrap(), iced::advanced::graphics::text::cosmic_text::Weight(1)).unwrap();
|
||||
// let width = f.metrics();
|
||||
// println!("Width: {width:?}");
|
||||
// iced::advanced::graphics::text::cosmic_text::Buffer::new(
|
||||
// font_sys.raw(),
|
||||
// Metrics::new(size.0, size.0),
|
||||
// ).layout_runs();
|
||||
// TODO: this needs to be computed from the font
|
||||
let col_width = 0.6 * size.0;
|
||||
|
||||
let rows = self.val.len().div_ceil(0x10);
|
||||
let row_label_length = rows.ilog2().div_ceil(4) as usize;
|
||||
let mut draw = |v: &str, pos: Point| {
|
||||
renderer.fill_text(
|
||||
Text {
|
||||
content: format!("{}", v),
|
||||
bounds: viewport.size(),
|
||||
size,
|
||||
line_height: size.into(),
|
||||
font,
|
||||
align_x: iced::advanced::text::Alignment::Left,
|
||||
align_y: Vertical::Top,
|
||||
shaping: iced::advanced::text::Shaping::Basic,
|
||||
wrapping: iced::advanced::text::Wrapping::None,
|
||||
hint_factor: None,
|
||||
},
|
||||
pos,
|
||||
Color::WHITE,
|
||||
layout.bounds(),
|
||||
);
|
||||
};
|
||||
draw("0", Point::new(0., 0.));
|
||||
for i in 0..0x10 {
|
||||
draw(
|
||||
"0",
|
||||
layout.position()
|
||||
+ Vector::new((3 + row_label_length + 3 * i) as f32 * col_width, 0.),
|
||||
);
|
||||
draw(
|
||||
"0",
|
||||
layout.position()
|
||||
+ Vector::new((4 + row_label_length + 3 * i) as f32 * col_width, 0.),
|
||||
);
|
||||
}
|
||||
// renderer.fill_text(
|
||||
// Text {
|
||||
// content: format!(
|
||||
// "{:width$} 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F {}",
|
||||
// "",
|
||||
// state.offset_y,
|
||||
// width = row_label_length
|
||||
// ),
|
||||
// bounds: viewport.size(),
|
||||
// size,
|
||||
// line_height: size.into(),
|
||||
// font,
|
||||
// align_x: iced_core::text::Alignment::Left,
|
||||
// align_y: iced_core::alignment::Vertical::Top,
|
||||
// shaping: iced_core::text::Shaping::Basic,
|
||||
// wrapping: iced_core::text::Wrapping::None,
|
||||
// hint_factor: None,
|
||||
// },
|
||||
// layout.position(),
|
||||
// Color::WHITE,
|
||||
// layout.bounds(),
|
||||
// );
|
||||
struct HexV(Option<u8>);
|
||||
impl Display for HexV {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self.0 {
|
||||
Some(v) => write!(f, "{:02X}", v),
|
||||
None => write!(f, "XX"),
|
||||
}
|
||||
}
|
||||
}
|
||||
// if rows > 0 {
|
||||
// rows.ilog2()
|
||||
// }
|
||||
let bounds = self.value_bounds(renderer, layout);
|
||||
let mut pos = bounds.position() + Vector::new(0., state.offset_y);
|
||||
for row in 0..rows {
|
||||
if bounds.contains(pos) || bounds.contains(pos + Vector::new(0., size.0 * LINE_HEIGHT))
|
||||
{
|
||||
renderer.fill_text(
|
||||
Text {
|
||||
content: format!(
|
||||
"{:0width$X}0: {} {} {} {} {} {} {} {} {} {} {} {} {} {} {} {}",
|
||||
row,
|
||||
HexV(self.val.peek(row * 0x10 + 0x0)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x1)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x2)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x3)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x4)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x5)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x6)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x7)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x8)),
|
||||
HexV(self.val.peek(row * 0x10 + 0x9)),
|
||||
HexV(self.val.peek(row * 0x10 + 0xA)),
|
||||
HexV(self.val.peek(row * 0x10 + 0xB)),
|
||||
HexV(self.val.peek(row * 0x10 + 0xC)),
|
||||
HexV(self.val.peek(row * 0x10 + 0xD)),
|
||||
HexV(self.val.peek(row * 0x10 + 0xE)),
|
||||
HexV(self.val.peek(row * 0x10 + 0xF)),
|
||||
width = row_label_length,
|
||||
),
|
||||
bounds: viewport.size(),
|
||||
size,
|
||||
line_height: size.into(),
|
||||
font,
|
||||
align_x: iced::advanced::text::Alignment::Left,
|
||||
align_y: Vertical::Top,
|
||||
shaping: iced::advanced::text::Shaping::Basic,
|
||||
wrapping: iced::advanced::text::Wrapping::None,
|
||||
hint_factor: None,
|
||||
},
|
||||
pos,
|
||||
Color::WHITE,
|
||||
bounds,
|
||||
);
|
||||
}
|
||||
pos += Vector::new(0., size.0 * LINE_HEIGHT);
|
||||
}
|
||||
|
||||
let scrollbar = self.scrollbar_bounds(renderer, layout);
|
||||
renderer.fill_quad(
|
||||
Quad {
|
||||
bounds: scrollbar,
|
||||
..Quad::default()
|
||||
},
|
||||
Color::BLACK,
|
||||
);
|
||||
let pos = state.offset_y / self.scroll_max(renderer, layout);
|
||||
renderer.fill_quad(
|
||||
Quad {
|
||||
bounds: Rectangle::new(
|
||||
Point::new(scrollbar.x, scrollbar.y - pos * (scrollbar.height - 20.)),
|
||||
Size::new(10., 20.),
|
||||
),
|
||||
..Quad::default()
|
||||
},
|
||||
Color::WHITE,
|
||||
);
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&mut self,
|
||||
_tree: &mut Tree,
|
||||
_layout: Layout<'_>,
|
||||
_renderer: &R,
|
||||
_operation: &mut dyn Operation,
|
||||
) {
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: &Event,
|
||||
layout: Layout<'_>,
|
||||
cursor: Cursor,
|
||||
renderer: &R,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, M>,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
// if !matches!(event, Event::Window(_)) {
|
||||
// println!("Event: {:#?}", event);
|
||||
// }
|
||||
match event {
|
||||
Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||
key: Key::Named(Named::PageUp),
|
||||
..
|
||||
}) => {
|
||||
let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
self.scroll(
|
||||
state,
|
||||
renderer,
|
||||
layout,
|
||||
&ScrollDelta::Pixels {
|
||||
x: 0.,
|
||||
y: self.value_bounds(renderer, layout).height,
|
||||
},
|
||||
);
|
||||
shell.request_redraw();
|
||||
shell.capture_event();
|
||||
}
|
||||
Event::Keyboard(iced::keyboard::Event::KeyPressed {
|
||||
key: Key::Named(Named::PageDown),
|
||||
..
|
||||
}) => {
|
||||
let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
self.scroll(
|
||||
state,
|
||||
renderer,
|
||||
layout,
|
||||
&ScrollDelta::Pixels {
|
||||
x: 0.,
|
||||
y: -self.value_bounds(renderer, layout).height,
|
||||
},
|
||||
);
|
||||
shell.request_redraw();
|
||||
shell.capture_event();
|
||||
}
|
||||
Event::Mouse(iced::mouse::Event::WheelScrolled { delta }) => {
|
||||
let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
let bounds = self.value_bounds(renderer, layout);
|
||||
if cursor.is_over(bounds) {
|
||||
self.scroll(state, renderer, layout, delta);
|
||||
shell.request_redraw();
|
||||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
Event::Mouse(iced::mouse::Event::ButtonPressed(Button::Left)) => {
|
||||
let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
let bounds = self.scrollbar_bounds(renderer, layout);
|
||||
if let Some(pos) = cursor.position_in(bounds) {
|
||||
state.offset_y = -(pos.y / bounds.height * self.scroll_max(renderer, layout));
|
||||
state.dragging = true;
|
||||
shell.request_redraw();
|
||||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
Event::Mouse(iced::mouse::Event::ButtonReleased(Button::Left)) => {
|
||||
let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
state.dragging = false;
|
||||
}
|
||||
// Event::Mouse(iced::mouse::Event::CursorLeft) => {
|
||||
// let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
// state.dragging = false;
|
||||
// }
|
||||
Event::Mouse(iced::mouse::Event::CursorMoved { .. }) => {
|
||||
let state: &mut HexEditorState = tree.state.downcast_mut();
|
||||
if state.dragging {
|
||||
let bounds = self.scrollbar_bounds(renderer, layout);
|
||||
if let Some(pos) = cursor.position_in(bounds) {
|
||||
state.offset_y =
|
||||
-(pos.y / bounds.height * self.scroll_max(renderer, layout));
|
||||
shell.request_redraw();
|
||||
shell.capture_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_tree: &Tree,
|
||||
_layout: Layout<'_>,
|
||||
_cursor: Cursor,
|
||||
_viewport: &Rectangle,
|
||||
_renderer: &R,
|
||||
) -> Interaction {
|
||||
Interaction::None
|
||||
}
|
||||
|
||||
fn overlay<'a>(
|
||||
&'a mut self,
|
||||
_tree: &'a mut Tree,
|
||||
_layout: Layout<'a>,
|
||||
_renderer: &R,
|
||||
_viewport: &Rectangle,
|
||||
_translation: Vector,
|
||||
) -> Option<overlay::Element<'a, M, T, R>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user