lots of refactoring
This commit is contained in:
parent
3377cc46df
commit
0130f14562
153
src/function.rs
153
src/function.rs
@ -3,19 +3,13 @@
|
|||||||
use crate::math_app::AppSettings;
|
use crate::math_app::AppSettings;
|
||||||
use crate::misc::*;
|
use crate::misc::*;
|
||||||
use crate::parsing::{process_func_str, BackingFunction};
|
use crate::parsing::{process_func_str, BackingFunction};
|
||||||
use crate::suggestions::{generate_hint, HintEnum};
|
use crate::widgets::AutoComplete;
|
||||||
use eframe::{egui, epaint};
|
use eframe::{egui, epaint};
|
||||||
use egui::{
|
use egui::{
|
||||||
plot::{BarChart, PlotUi, Value},
|
plot::{BarChart, PlotUi, Value},
|
||||||
text::CCursor,
|
|
||||||
text_edit::CursorRange,
|
|
||||||
widgets::plot::Bar,
|
widgets::plot::Bar,
|
||||||
Key, TextEdit, Widget,
|
|
||||||
};
|
|
||||||
use epaint::{
|
|
||||||
text::cursor::{Cursor, PCursor, RCursor},
|
|
||||||
Color32,
|
|
||||||
};
|
};
|
||||||
|
use epaint::Color32;
|
||||||
use std::fmt::{self, Debug};
|
use std::fmt::{self, Debug};
|
||||||
|
|
||||||
#[cfg(threading)]
|
#[cfg(threading)]
|
||||||
@ -38,37 +32,6 @@ lazy_static::lazy_static! {
|
|||||||
pub static ref DEFAULT_FUNCTION_ENTRY: FunctionEntry = FunctionEntry::default();
|
pub static ref DEFAULT_FUNCTION_ENTRY: FunctionEntry = FunctionEntry::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct AutoComplete {
|
|
||||||
pub i: usize,
|
|
||||||
pub hint: HintEnum<'static>,
|
|
||||||
pub func_str: Option<String>,
|
|
||||||
pub changed: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for AutoComplete {
|
|
||||||
fn default() -> AutoComplete {
|
|
||||||
AutoComplete {
|
|
||||||
i: 0,
|
|
||||||
hint: HintEnum::None,
|
|
||||||
func_str: None,
|
|
||||||
changed: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AutoComplete {
|
|
||||||
fn changed(&mut self, string: String) {
|
|
||||||
if self.func_str != Some(string.clone()) {
|
|
||||||
self.changed = true;
|
|
||||||
self.func_str = Some(string.clone());
|
|
||||||
self.hint = generate_hint(string);
|
|
||||||
} else {
|
|
||||||
self.changed = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `FunctionEntry` is a function that can calculate values, integrals,
|
/// `FunctionEntry` is a function that can calculate values, integrals,
|
||||||
/// derivatives, etc etc
|
/// derivatives, etc etc
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -130,117 +93,15 @@ impl FunctionEntry {
|
|||||||
pub fn auto_complete(
|
pub fn auto_complete(
|
||||||
&mut self, ui: &mut egui::Ui, string: &mut String,
|
&mut self, ui: &mut egui::Ui, string: &mut String,
|
||||||
) -> (bool, bool, Option<String>) {
|
) -> (bool, bool, Option<String>) {
|
||||||
// Put here so these key presses don't interact with other elements
|
let (output_string, in_focus) = self.autocomplete.ui(ui, string.to_string());
|
||||||
let enter_pressed = ui
|
|
||||||
.input_mut()
|
|
||||||
.consume_key(egui::Modifiers::NONE, Key::Enter);
|
|
||||||
let tab_pressed = ui.input_mut().consume_key(egui::Modifiers::NONE, Key::Tab);
|
|
||||||
|
|
||||||
let te_id = ui.make_persistent_id("text_edit_ac".to_string());
|
let changed = output_string != *string;
|
||||||
|
|
||||||
// update self.autocomplete
|
|
||||||
self.autocomplete.changed(string.clone());
|
|
||||||
|
|
||||||
let mut func_edit = egui::TextEdit::singleline(string)
|
|
||||||
.hint_forward(true)
|
|
||||||
.lock_focus(true);
|
|
||||||
|
|
||||||
if self.autocomplete.hint.is_none() {
|
|
||||||
func_edit.id(te_id).ui(ui);
|
|
||||||
return (false, false, self.get_test_result());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(single_hint) = self.autocomplete.hint.get_single() {
|
|
||||||
let func_edit_2 = func_edit;
|
|
||||||
func_edit = func_edit_2.hint_text(&single_hint);
|
|
||||||
}
|
|
||||||
|
|
||||||
let re = func_edit.id(te_id).ui(ui);
|
|
||||||
|
|
||||||
let func_edit_focus = re.has_focus();
|
|
||||||
|
|
||||||
// If in focus and right arrow key was pressed, apply hint
|
|
||||||
if func_edit_focus {
|
|
||||||
let mut push_cursor: bool = false;
|
|
||||||
let apply_key = ui.input().key_pressed(Key::ArrowRight) | enter_pressed | tab_pressed;
|
|
||||||
|
|
||||||
if apply_key && let Some(single_hint) = self.autocomplete.hint.get_single() {
|
|
||||||
push_cursor = true;
|
|
||||||
*string = string.clone() + &single_hint;
|
|
||||||
} else if self.autocomplete.hint.is_multi() {
|
|
||||||
let selections = self.autocomplete.hint.ensure_many();
|
|
||||||
|
|
||||||
let max_i = selections.len() as i16 - 1;
|
|
||||||
|
|
||||||
let mut i = self.autocomplete.i as i16;
|
|
||||||
|
|
||||||
if ui.input().key_pressed(Key::ArrowDown) {
|
|
||||||
i += 1;
|
|
||||||
if i > max_i {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
} else if ui.input().key_pressed(Key::ArrowUp) {
|
|
||||||
i -= 1;
|
|
||||||
if 0 > i {
|
|
||||||
i = max_i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.autocomplete.i = i as usize;
|
|
||||||
|
|
||||||
let popup_id = ui.make_persistent_id("autocomplete_popup");
|
|
||||||
|
|
||||||
let mut clicked = false;
|
|
||||||
|
|
||||||
egui::popup_below_widget(ui, popup_id, &re, |ui| {
|
|
||||||
for (i, candidate) in selections.iter().enumerate() {
|
|
||||||
if ui
|
|
||||||
.selectable_label(i == self.autocomplete.i, *candidate)
|
|
||||||
.clicked()
|
|
||||||
{
|
|
||||||
clicked = true;
|
|
||||||
self.autocomplete.i = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if clicked | apply_key {
|
|
||||||
*string += selections[self.autocomplete.i];
|
|
||||||
push_cursor = true;
|
|
||||||
|
|
||||||
|
|
||||||
// don't need this here as it simply won't be display next frame in `math_app.rs`
|
|
||||||
// ui.memory().close_popup();
|
|
||||||
} else {
|
|
||||||
ui.memory().open_popup(popup_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push cursor to end if needed
|
|
||||||
if push_cursor {
|
|
||||||
let mut state = TextEdit::load_state(ui.ctx(), te_id).unwrap();
|
|
||||||
state.set_cursor_range(Some(CursorRange::one(Cursor {
|
|
||||||
ccursor: CCursor {
|
|
||||||
index: 0,
|
|
||||||
prefer_next_row: false,
|
|
||||||
},
|
|
||||||
rcursor: RCursor { row: 0, column: 0 },
|
|
||||||
pcursor: PCursor {
|
|
||||||
paragraph: 0,
|
|
||||||
offset: 10000,
|
|
||||||
prefer_next_row: false,
|
|
||||||
},
|
|
||||||
})));
|
|
||||||
TextEdit::store_state(ui.ctx(), te_id, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let changed = *string != self.get_func_raw();
|
|
||||||
if changed {
|
if changed {
|
||||||
self.update_string(&*string);
|
*string = output_string.clone();
|
||||||
|
self.update_string(&output_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
(func_edit_focus, changed, self.get_test_result())
|
(in_focus, changed, self.get_test_result())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_test_result(&self) -> Option<String> { self.test_result.clone() }
|
pub fn get_test_result(&self) -> Option<String> { self.test_result.clone() }
|
||||||
|
|||||||
@ -11,6 +11,7 @@ mod math_app;
|
|||||||
mod misc;
|
mod misc;
|
||||||
mod parsing;
|
mod parsing;
|
||||||
mod suggestions;
|
mod suggestions;
|
||||||
|
mod widgets;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(target_arch = "wasm32")] {
|
if #[cfg(target_arch = "wasm32")] {
|
||||||
|
|||||||
@ -11,6 +11,7 @@ mod math_app;
|
|||||||
mod misc;
|
mod misc;
|
||||||
mod parsing;
|
mod parsing;
|
||||||
mod suggestions;
|
mod suggestions;
|
||||||
|
mod widgets;
|
||||||
|
|
||||||
// For running the program natively! (Because why not?)
|
// For running the program natively! (Because why not?)
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use egui::{
|
|||||||
FontFamily, Key, RichText, SidePanel, Slider, TopBottomPanel, Vec2, Visuals, Window,
|
FontFamily, Key, RichText, SidePanel, Slider, TopBottomPanel, Vec2, Visuals, Window,
|
||||||
};
|
};
|
||||||
use instant::Duration;
|
use instant::Duration;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::{collections::BTreeMap, io::Read, ops::BitXorAssign, str};
|
use std::{collections::BTreeMap, io::Read, ops::BitXorAssign, str};
|
||||||
|
|
||||||
#[cfg(threading)]
|
#[cfg(threading)]
|
||||||
@ -291,20 +292,14 @@ pub struct MathApp {
|
|||||||
/// took for the last frame (the Duration). Stored in a Tuple.
|
/// took for the last frame (the Duration). Stored in a Tuple.
|
||||||
last_info: (Vec<Option<f64>>, Duration),
|
last_info: (Vec<Option<f64>>, Duration),
|
||||||
|
|
||||||
/// Stores whether or not the Help window is open
|
|
||||||
help_open: bool,
|
|
||||||
|
|
||||||
/// Stores whether or not the Info window is open
|
|
||||||
info_open: bool,
|
|
||||||
|
|
||||||
/// Stores whether or not dark mode is enabled
|
/// Stores whether or not dark mode is enabled
|
||||||
dark_mode: bool,
|
dark_mode: bool,
|
||||||
|
|
||||||
/// Stores whether or not the text boxes are focused
|
/// Stores whether or not the text boxes are focused
|
||||||
text_boxes_focused: bool,
|
text_boxes_focused: bool,
|
||||||
|
|
||||||
/// Stores whether or not the side panel is shown or not
|
/// Stores opened windows/elements for later reference
|
||||||
show_side_panel: bool,
|
opened: HashMap<&'static str, bool>,
|
||||||
|
|
||||||
/// Stores settings (pretty self-explanatory)
|
/// Stores settings (pretty self-explanatory)
|
||||||
settings: AppSettings,
|
settings: AppSettings,
|
||||||
@ -318,11 +313,9 @@ impl Default for MathApp {
|
|||||||
func_errors: vec![None],
|
func_errors: vec![None],
|
||||||
exists_error: false,
|
exists_error: false,
|
||||||
last_info: (vec![None], Duration::ZERO),
|
last_info: (vec![None], Duration::ZERO),
|
||||||
help_open: true,
|
|
||||||
info_open: false,
|
|
||||||
dark_mode: true,
|
dark_mode: true,
|
||||||
text_boxes_focused: false,
|
text_boxes_focused: false,
|
||||||
show_side_panel: true,
|
opened: HashMap::from([("help", true), ("info", false), ("side_panel", true)]),
|
||||||
settings: AppSettings::default(),
|
settings: AppSettings::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,6 +349,10 @@ impl MathApp {
|
|||||||
Self::default() // initialize `MathApp`
|
Self::default() // initialize `MathApp`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_opened_mut(&mut self, id: &str) -> &mut bool { self.opened.get_mut(id).unwrap() }
|
||||||
|
|
||||||
|
fn get_opened(&self, id: &str) -> bool { *self.opened.get(id).unwrap() }
|
||||||
|
|
||||||
/// Creates SidePanel which contains configuration options
|
/// Creates SidePanel which contains configuration options
|
||||||
fn side_panel(&mut self, ctx: &Context) {
|
fn side_panel(&mut self, ctx: &Context) {
|
||||||
// Side Panel which contains vital options to the operation of the application
|
// Side Panel which contains vital options to the operation of the application
|
||||||
@ -555,7 +552,7 @@ impl epi::App for MathApp {
|
|||||||
// this is behind this check as if it wasn't, it would trigger if the user
|
// this is behind this check as if it wasn't, it would trigger if the user
|
||||||
// presses the h key in a text box as well
|
// presses the h key in a text box as well
|
||||||
if !self.text_boxes_focused {
|
if !self.text_boxes_focused {
|
||||||
self.show_side_panel
|
self.get_opened_mut("side_panel")
|
||||||
.bitxor_assign(ctx.input().key_down(Key::H));
|
.bitxor_assign(ctx.input().key_down(Key::H));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -566,9 +563,10 @@ impl epi::App for MathApp {
|
|||||||
TopBottomPanel::top("top_bar").show(ctx, |ui| {
|
TopBottomPanel::top("top_bar").show(ctx, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
// Button in top bar to toggle showing the side panel
|
// Button in top bar to toggle showing the side panel
|
||||||
self.show_side_panel.bitxor_assign(
|
let side_curr_open = self.get_opened("help");
|
||||||
|
self.get_opened_mut("side_panel").bitxor_assign(
|
||||||
ui.add(Button::new("Panel"))
|
ui.add(Button::new("Panel"))
|
||||||
.on_hover_text(match self.show_side_panel {
|
.on_hover_text(match side_curr_open {
|
||||||
true => "Hide Side Panel",
|
true => "Hide Side Panel",
|
||||||
false => "Show Side Panel",
|
false => "Show Side Panel",
|
||||||
})
|
})
|
||||||
@ -587,9 +585,10 @@ impl epi::App for MathApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Toggles opening the Help window
|
// Toggles opening the Help window
|
||||||
self.help_open.bitxor_assign(
|
let help_curr_open = self.get_opened("help");
|
||||||
|
self.get_opened_mut("help").bitxor_assign(
|
||||||
ui.add(Button::new("Help"))
|
ui.add(Button::new("Help"))
|
||||||
.on_hover_text(match self.help_open {
|
.on_hover_text(match help_curr_open {
|
||||||
true => "Close Help Window",
|
true => "Close Help Window",
|
||||||
false => "Open Help Window",
|
false => "Open Help Window",
|
||||||
})
|
})
|
||||||
@ -597,9 +596,10 @@ impl epi::App for MathApp {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Toggles opening the Info window
|
// Toggles opening the Info window
|
||||||
self.info_open.bitxor_assign(
|
let info_curr_open = self.get_opened("info");
|
||||||
|
self.get_opened_mut("info").bitxor_assign(
|
||||||
ui.add(Button::new("Info"))
|
ui.add(Button::new("Info"))
|
||||||
.on_hover_text(match self.info_open {
|
.on_hover_text(match info_curr_open {
|
||||||
true => "Close Info Window",
|
true => "Close Info Window",
|
||||||
false => "Open Info Window",
|
false => "Open Info Window",
|
||||||
})
|
})
|
||||||
@ -631,7 +631,7 @@ impl epi::App for MathApp {
|
|||||||
// Help window with information for users
|
// Help window with information for users
|
||||||
Window::new("Help")
|
Window::new("Help")
|
||||||
.default_pos([200.0, 200.0])
|
.default_pos([200.0, 200.0])
|
||||||
.open(&mut self.help_open)
|
.open(self.get_opened_mut("help"))
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.collapsible(false)
|
.collapsible(false)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
@ -661,7 +661,7 @@ impl epi::App for MathApp {
|
|||||||
// Window with information about the build and current commit
|
// Window with information about the build and current commit
|
||||||
Window::new("Info")
|
Window::new("Info")
|
||||||
.default_pos([200.0, 200.0])
|
.default_pos([200.0, 200.0])
|
||||||
.open(&mut self.info_open)
|
.open(self.get_opened_mut("info"))
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.collapsible(false)
|
.collapsible(false)
|
||||||
.show(ctx, |ui| {
|
.show(ctx, |ui| {
|
||||||
@ -669,7 +669,7 @@ impl epi::App for MathApp {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// If side panel is enabled, show it.
|
// If side panel is enabled, show it.
|
||||||
if self.show_side_panel {
|
if self.get_opened("side_panel") {
|
||||||
self.side_panel(ctx);
|
self.side_panel(ctx);
|
||||||
} else {
|
} else {
|
||||||
self.text_boxes_focused = false;
|
self.text_boxes_focused = false;
|
||||||
|
|||||||
142
src/widgets.rs
Normal file
142
src/widgets.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
use crate::suggestions::{generate_hint, HintEnum};
|
||||||
|
use eframe::{egui, epaint};
|
||||||
|
use egui::{text::CCursor, text_edit::CursorRange, Key, Modifiers, TextEdit, Widget};
|
||||||
|
use epaint::text::cursor::{Cursor, PCursor, RCursor};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AutoComplete {
|
||||||
|
pub i: usize,
|
||||||
|
pub hint: HintEnum<'static>,
|
||||||
|
pub func_str: Option<String>,
|
||||||
|
pub changed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AutoComplete {
|
||||||
|
fn default() -> AutoComplete {
|
||||||
|
AutoComplete {
|
||||||
|
i: 0,
|
||||||
|
hint: HintEnum::None,
|
||||||
|
func_str: None,
|
||||||
|
changed: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoComplete {
|
||||||
|
fn changed(&mut self, string: String) {
|
||||||
|
if self.func_str != Some(string.clone()) {
|
||||||
|
self.changed = true;
|
||||||
|
self.func_str = Some(string.clone());
|
||||||
|
self.hint = generate_hint(string);
|
||||||
|
} else {
|
||||||
|
self.changed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(&mut self, ui: &mut egui::Ui, string: String) -> (String, bool) {
|
||||||
|
let mut new_string = string.clone();
|
||||||
|
// Put here so these key presses don't interact with other elements
|
||||||
|
let enter_pressed = ui.input_mut().consume_key(Modifiers::NONE, Key::Enter);
|
||||||
|
let tab_pressed = ui.input_mut().consume_key(Modifiers::NONE, Key::Tab);
|
||||||
|
|
||||||
|
let te_id = ui.make_persistent_id("text_edit_ac".to_string());
|
||||||
|
|
||||||
|
// update self
|
||||||
|
self.changed(string.clone());
|
||||||
|
|
||||||
|
let mut func_edit = egui::TextEdit::singleline(&mut new_string)
|
||||||
|
.hint_forward(true)
|
||||||
|
.lock_focus(true);
|
||||||
|
|
||||||
|
if self.hint.is_none() {
|
||||||
|
return (string, func_edit.id(te_id).ui(ui).has_focus());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(single_hint) = self.hint.get_single() {
|
||||||
|
let func_edit_2 = func_edit;
|
||||||
|
func_edit = func_edit_2.hint_text(&single_hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
let re = func_edit.id(te_id).ui(ui);
|
||||||
|
|
||||||
|
let func_edit_focus = re.has_focus();
|
||||||
|
|
||||||
|
// If in focus and right arrow key was pressed, apply hint
|
||||||
|
if func_edit_focus {
|
||||||
|
let mut push_cursor: bool = false;
|
||||||
|
let apply_key = ui.input().key_pressed(Key::ArrowRight) | enter_pressed | tab_pressed;
|
||||||
|
|
||||||
|
if apply_key && let Some(single_hint) = self.hint.get_single() {
|
||||||
|
push_cursor = true;
|
||||||
|
new_string = string + &single_hint;
|
||||||
|
} else if self.hint.is_multi() {
|
||||||
|
let selections = self.hint.ensure_many();
|
||||||
|
|
||||||
|
let max_i = selections.len() as i16 - 1;
|
||||||
|
|
||||||
|
let mut i = self.i as i16;
|
||||||
|
|
||||||
|
if ui.input().key_pressed(Key::ArrowDown) {
|
||||||
|
i += 1;
|
||||||
|
if i > max_i {
|
||||||
|
i = 0;
|
||||||
|
}
|
||||||
|
} else if ui.input().key_pressed(Key::ArrowUp) {
|
||||||
|
i -= 1;
|
||||||
|
if 0 > i {
|
||||||
|
i = max_i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.i = i as usize;
|
||||||
|
|
||||||
|
let popup_id = ui.make_persistent_id("autocomplete_popup");
|
||||||
|
|
||||||
|
let mut clicked = false;
|
||||||
|
|
||||||
|
egui::popup_below_widget(ui, popup_id, &re, |ui| {
|
||||||
|
for (i, candidate) in selections.iter().enumerate() {
|
||||||
|
if ui
|
||||||
|
.selectable_label(i == self.i, *candidate)
|
||||||
|
.clicked()
|
||||||
|
{
|
||||||
|
clicked = true;
|
||||||
|
self.i = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if clicked | apply_key {
|
||||||
|
new_string += selections[self.i];
|
||||||
|
push_cursor = true;
|
||||||
|
|
||||||
|
|
||||||
|
// don't need this here as it simply won't be display next frame in `math_app.rs`
|
||||||
|
// ui.memory().close_popup();
|
||||||
|
} else {
|
||||||
|
ui.memory().open_popup(popup_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push cursor to end if needed
|
||||||
|
if push_cursor {
|
||||||
|
let mut state = TextEdit::load_state(ui.ctx(), te_id).unwrap();
|
||||||
|
state.set_cursor_range(Some(CursorRange::one(Cursor {
|
||||||
|
ccursor: CCursor {
|
||||||
|
index: 0,
|
||||||
|
prefer_next_row: false,
|
||||||
|
},
|
||||||
|
rcursor: RCursor { row: 0, column: 0 },
|
||||||
|
pcursor: PCursor {
|
||||||
|
paragraph: 0,
|
||||||
|
offset: 10000,
|
||||||
|
prefer_next_row: false,
|
||||||
|
},
|
||||||
|
})));
|
||||||
|
TextEdit::store_state(ui.ctx(), te_id, state);
|
||||||
|
}
|
||||||
|
return (new_string, true);
|
||||||
|
}
|
||||||
|
(string, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user