This commit is contained in:
parent
11bbb2500f
commit
a7f3c12018
5 changed files with 460 additions and 498 deletions
482
Cargo.lock
generated
482
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
14
Cargo.toml
14
Cargo.toml
|
@ -7,8 +7,10 @@ rust-version = "1.72"
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-jotdown = { git = "https://git.nations.lol/fnmain/cosmic-jotdown" }
|
# cosmic-jotdown = { git = "https://git.nations.lol/fnmain/cosmic-jotdown" }
|
||||||
eframe = { version = "0.26.2", default-features = false, features = [
|
cosmic-jotdown = {path = "../cosmic-jotdown"}
|
||||||
|
jotdown = { git = "https://git.nations.lol/fnmain/jotdown" }
|
||||||
|
eframe = { version = "0.27.2", default-features = false, features = [
|
||||||
"accesskit", # Make egui comptaible with screen readers. NOTE: adds a lot of dependencies.
|
"accesskit", # Make egui comptaible with screen readers. NOTE: adds a lot of dependencies.
|
||||||
# "default_fonts",
|
# "default_fonts",
|
||||||
"wgpu", # Use the glow rendering backend. Alternative: "wgpu".
|
"wgpu", # Use the glow rendering backend. Alternative: "wgpu".
|
||||||
|
@ -16,17 +18,17 @@ eframe = { version = "0.26.2", default-features = false, features = [
|
||||||
"wayland",
|
"wayland",
|
||||||
] }
|
] }
|
||||||
egui-glyphon = { git = "https://git.nations.lol/fnmain/egui-glyphon" }
|
egui-glyphon = { git = "https://git.nations.lol/fnmain/egui-glyphon" }
|
||||||
egui_extras = { version = "0.26.2", features = ["image", "http", "file"] }
|
egui_extras = { version = "0.27.2", features = ["image", "http", "file"] }
|
||||||
encase = { version = "0.7.0", features = ["glam"] }
|
encase = { version = "0.7.0", features = ["glam"] }
|
||||||
glam = "0.25.0"
|
glam = "0.25.0"
|
||||||
image = { version = "0.24.9", features = ["jpeg", "png"] }
|
image = { version = "0.24.9", features = ["jpeg", "png"], defaut-features = false }
|
||||||
keyframe = { version = "1.1.1", default-features = false }
|
keyframe = { version = "1.1.1", default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
range-map = "0.2.0"
|
rangemap = "1.5.1"
|
||||||
|
|
||||||
# native:
|
# native:
|
||||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||||
env_logger = "0.10"
|
env_logger = "0.11"
|
||||||
|
|
||||||
# web:
|
# web:
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 3.5 MiB After Width: | Height: | Size: 3.7 MiB |
|
@ -12,4 +12,4 @@
|
||||||
- I balance the time I spend at my computer with outdoor activities like, camping, hiking, and backpacking
|
- I balance the time I spend at my computer with outdoor activities like, camping, hiking, and backpacking
|
||||||
- I know how to both write code for computers, as well as whip them into shape when they start having problems. I am the IT guy in my family
|
- I know how to both write code for computers, as well as whip them into shape when they start having problems. I am the IT guy in my family
|
||||||
|
|
||||||
\*Special thanks to _meico_ from Shadertoy for the GLSL version of your Spectrum Ring shader
|
\*Special thanks to _meico_ from Shadertoy for the GLSL version of your [Spectrum Ring shader](https://www.shadertoy.com/view/XssXWH)
|
||||||
|
|
460
src/app.rs
460
src/app.rs
|
@ -13,12 +13,11 @@ use std::sync::Arc;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
use arc_swap::ArcSwapAny;
|
use arc_swap::ArcSwapAny;
|
||||||
use cosmic_jotdown::jotdown::{self, Event, ListKind};
|
use cosmic_jotdown::Indent;
|
||||||
use cosmic_jotdown::{Indent, INDENT_AMOUNT};
|
|
||||||
use eframe::egui::mutex::{Mutex, RwLock};
|
use eframe::egui::mutex::{Mutex, RwLock};
|
||||||
use eframe::egui::{
|
use eframe::egui::{
|
||||||
self, lerp, Align2, Id, Image, ImageSize, ImageSource, LayerId, OpenUrl, Pos2, Rect, Rounding,
|
self, lerp, Align2, Id, Image, ImageSize, ImageSource, LayerId, OpenUrl, Pos2, Rect, Rounding,
|
||||||
Sense, Stroke, Vec2,
|
Sense, Vec2,
|
||||||
};
|
};
|
||||||
use eframe::egui_wgpu::{self, wgpu};
|
use eframe::egui_wgpu::{self, wgpu};
|
||||||
use egui::{Color32, Frame};
|
use egui::{Color32, Frame};
|
||||||
|
@ -28,8 +27,9 @@ use egui_glyphon::{BufferWithTextArea, GlyphonRenderer, GlyphonRendererCallback}
|
||||||
use encase::ShaderType;
|
use encase::ShaderType;
|
||||||
use glam::Mat2;
|
use glam::Mat2;
|
||||||
use glyphon::{Buffer, FontSystem, Metrics};
|
use glyphon::{Buffer, FontSystem, Metrics};
|
||||||
|
use jotdown::Event;
|
||||||
use keyframe::functions;
|
use keyframe::functions;
|
||||||
use range_map::RangeMap;
|
use rangemap::RangeMap;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
/// We derive Deserialize/Serialize so we can persist app state on shutdown.
|
||||||
|
@ -68,7 +68,7 @@ pub enum State {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ContextWindow {
|
pub struct ContextWindow {
|
||||||
pub size: Vec2,
|
pub size: Vec2,
|
||||||
pub text: Vec<(Rect, Indent, ContextBlock)>,
|
pub text: Vec<ResolvedItem<'static>>,
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
}
|
}
|
||||||
|
@ -80,16 +80,6 @@ pub struct ContextIcon {
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum ContextBlock {
|
|
||||||
Buffer(Arc<RwLock<Buffer>>, Option<RangeMap<usize, &'static str>>),
|
|
||||||
Image {
|
|
||||||
alt_text: Arc<RwLock<Buffer>>,
|
|
||||||
image: Image<'static>,
|
|
||||||
hi_image: Image<'static>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const CONTEXT_METRICS: Metrics = Metrics::new(16.0, 18.0);
|
const CONTEXT_METRICS: Metrics = Metrics::new(16.0, 18.0);
|
||||||
const HOVER_TIME: f64 = 0.5;
|
const HOVER_TIME: f64 = 0.5;
|
||||||
|
|
||||||
|
@ -231,6 +221,14 @@ const NAME_PLATE: [&str; 3] = [
|
||||||
"My portfolio",
|
"My portfolio",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
pub struct ResolvedItem<'a> {
|
||||||
|
pub indent: Indent,
|
||||||
|
pub buffer: Arc<RwLock<Buffer>>,
|
||||||
|
pub relative_bounds: Rect,
|
||||||
|
pub url_map: Option<RangeMap<usize, Cow<'a, str>>>,
|
||||||
|
pub image_urls: Option<(String, String)>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ContextWindow {
|
impl ContextWindow {
|
||||||
pub fn set_content(
|
pub fn set_content(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -238,119 +236,34 @@ impl ContextWindow {
|
||||||
#[cfg(target_arch = "wasm32")] transform: Option<Mat2>,
|
#[cfg(target_arch = "wasm32")] transform: Option<Mat2>,
|
||||||
#[cfg(target_arch = "wasm32")] name: Option<&'static str>,
|
#[cfg(target_arch = "wasm32")] name: Option<&'static str>,
|
||||||
font_system: &mut FontSystem,
|
font_system: &mut FontSystem,
|
||||||
mut max_width: f32,
|
max_width: f32,
|
||||||
#[cfg(target_arch = "wasm32")] states: &mut Vec<State>,
|
#[cfg(target_arch = "wasm32")] states: &mut Vec<State>,
|
||||||
) {
|
) {
|
||||||
self.size = Vec2::new(max_width / 1.5, 0.0);
|
self.size = Vec2::new(max_width / 1.5, 0.0);
|
||||||
let mut last_indent = None;
|
let text =
|
||||||
let mut last_image_size: Option<Vec2> = None;
|
cosmic_jotdown::jotdown_into_buffers(content.iter().cloned()).collect::<Vec<_>>();
|
||||||
max_width /= 1.5;
|
|
||||||
self.text = cosmic_jotdown::jotdown_into_buffers(
|
let (size, text) = cosmic_jotdown::resolve_paragraphs(
|
||||||
content.iter().cloned(),
|
&text,
|
||||||
|
self.size,
|
||||||
font_system,
|
font_system,
|
||||||
CONTEXT_METRICS,
|
CONTEXT_METRICS,
|
||||||
max_width,
|
None,
|
||||||
)
|
1.0,
|
||||||
.map(|buffer| {
|
env!("PHOST"),
|
||||||
let measurement = measure_buffer(&buffer.buffer, self.size);
|
);
|
||||||
let paragraph_height = if last_indent.is_none() || buffer.indent.modifier.is_none() {
|
|
||||||
CONTEXT_METRICS.line_height * 1.5
|
|
||||||
} else {
|
|
||||||
8.0
|
|
||||||
};
|
|
||||||
last_indent = buffer.indent.modifier;
|
|
||||||
let buffer = if let Some(url) = buffer.image_url {
|
|
||||||
let image;
|
|
||||||
let hi_image;
|
|
||||||
let url = url.split_once('#').unwrap();
|
|
||||||
let size = url.1.split_once('x').unwrap();
|
|
||||||
let size = Vec2::new(size.0.parse().unwrap(), size.1.parse().unwrap());
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
image = Image::from_uri(format!(concat!(env!("PHOST"), "/{}"), url.0));
|
|
||||||
let split = url.0.rsplit_once('.').unwrap();
|
|
||||||
hi_image = Image::from_uri(format!(
|
|
||||||
concat!(env!("PHOST"), "/{}_hi.{}"),
|
|
||||||
split.0, split.1
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
image = Image::from_uri(format!("file://assets/{}", url.0));
|
|
||||||
let split = url.0.rsplit_once('.').unwrap();
|
|
||||||
hi_image = Image::from_uri(format!("file://assets/{}_hi.{}", split.0, split.1));
|
|
||||||
}
|
|
||||||
let mut res = (
|
|
||||||
Rect::from_min_size(
|
|
||||||
Pos2::new(buffer.indent.indent, self.size.y + paragraph_height),
|
|
||||||
size,
|
|
||||||
),
|
|
||||||
buffer.indent,
|
|
||||||
ContextBlock::Image {
|
|
||||||
alt_text: Arc::new(RwLock::new(buffer.buffer)),
|
|
||||||
image,
|
|
||||||
hi_image,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
const IMAGE_PADDING: f32 = 8.0;
|
|
||||||
if let Some(last_size) = last_image_size.as_mut() {
|
|
||||||
let ls = *last_size;
|
|
||||||
last_size.x += size.x + IMAGE_PADDING;
|
|
||||||
|
|
||||||
if last_size.x > max_width {
|
self.text = text
|
||||||
self.size.y += last_size.y + paragraph_height;
|
.into_iter()
|
||||||
last_size.x = size.x + IMAGE_PADDING;
|
.map(|t| ResolvedItem {
|
||||||
last_size.y = size.y;
|
indent: t.indent,
|
||||||
res.0 = Rect::from_min_size(
|
buffer: Arc::new(RwLock::new(t.buffer)),
|
||||||
Pos2::new(buffer.indent.indent, self.size.y + paragraph_height),
|
relative_bounds: t.relative_bounds,
|
||||||
size,
|
url_map: t.url_map,
|
||||||
);
|
image_urls: t.image_urls,
|
||||||
} else {
|
})
|
||||||
last_size.y = last_size.y.max(size.y);
|
.collect();
|
||||||
res.0 = res.0.translate(Vec2::new(ls.x, 0.0));
|
self.size = size;
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if size.x > max_width {
|
|
||||||
let max_size = Vec2::new(max_width, size.y);
|
|
||||||
let new_size = ImageSize {
|
|
||||||
max_size,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.calc_size(max_size, size);
|
|
||||||
res.0 = Rect::from_min_size(
|
|
||||||
Pos2::new(buffer.indent.indent, self.size.y + paragraph_height),
|
|
||||||
new_size,
|
|
||||||
);
|
|
||||||
self.size.y += new_size.y + paragraph_height;
|
|
||||||
} else {
|
|
||||||
last_image_size = Some(size + Vec2::new(IMAGE_PADDING, 0.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res
|
|
||||||
} else {
|
|
||||||
if let Some(size) = last_image_size {
|
|
||||||
self.size.y += size.y + paragraph_height;
|
|
||||||
}
|
|
||||||
let res = (
|
|
||||||
Rect::from_min_size(
|
|
||||||
Pos2::new(buffer.indent.indent, self.size.y + paragraph_height),
|
|
||||||
measurement.size(),
|
|
||||||
),
|
|
||||||
buffer.indent,
|
|
||||||
ContextBlock::Buffer(Arc::new(RwLock::new(buffer.buffer)), buffer.url_map),
|
|
||||||
);
|
|
||||||
last_image_size = None;
|
|
||||||
self.size.y += measurement.height() + paragraph_height;
|
|
||||||
res
|
|
||||||
};
|
|
||||||
self.size.x = self.size.x.max(measurement.width());
|
|
||||||
buffer
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
if let Some(size) = last_image_size {
|
|
||||||
self.size.y += size.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
if let Some(name) = name {
|
if let Some(name) = name {
|
||||||
|
@ -743,196 +656,173 @@ impl eframe::App for Portfolio {
|
||||||
Rounding::same(25.0),
|
Rounding::same(25.0),
|
||||||
Color32::BLACK.gamma_multiply(lerp(0.0..=0.5, zoom_view_opacity)),
|
Color32::BLACK.gamma_multiply(lerp(0.0..=0.5, zoom_view_opacity)),
|
||||||
);
|
);
|
||||||
self.window
|
self.window.text.iter().for_each(|context_block| {
|
||||||
.text
|
if let Some(image_urls) = &context_block.image_urls {
|
||||||
.iter()
|
let image_rect =
|
||||||
.for_each(|context_block| match &context_block.2 {
|
context_block.relative_bounds.translate(rect.min.to_vec2());
|
||||||
ContextBlock::Buffer(buffer, url_map) => {
|
|
||||||
let text_rect = context_block.0.translate(rect.min.to_vec2());
|
|
||||||
// ui.painter().debug_rect(text_rect, Color32::GREEN, "");
|
|
||||||
if let Some(url_map) = url_map {
|
|
||||||
let text_response = ui.allocate_rect(text_rect, Sense::click());
|
|
||||||
let mut buffer = buffer.write();
|
|
||||||
let mut editor =
|
|
||||||
Editor::new(BufferRef::Borrowed(buffer.deref_mut()));
|
|
||||||
if text_response.hovered()
|
|
||||||
&& ui.input(|i| i.raw_scroll_delta == Vec2::ZERO)
|
|
||||||
{
|
|
||||||
let mouse_pos = ui
|
|
||||||
.input(|i| i.pointer.latest_pos().unwrap_or_default())
|
|
||||||
- text_rect.min.to_vec2();
|
|
||||||
|
|
||||||
editor.action(
|
let image_response = ui.allocate_rect(image_rect, Sense::click());
|
||||||
self.font_system.lock().deref_mut(),
|
let image_response_clicked = image_response.clicked();
|
||||||
glyphon::Action::Click {
|
|
||||||
x: mouse_pos.x as i32,
|
|
||||||
y: mouse_pos.y as i32 - 3,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut location = editor.cursor();
|
let time_offset = ui.memory_mut(|m| {
|
||||||
match location.affinity {
|
let time_offset =
|
||||||
glyphon::Affinity::After => location.index += 1,
|
m.data.get_temp_mut_or_default::<f64>(image_response.id);
|
||||||
glyphon::Affinity::Before => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if url_map.get(location.index).is_some() {
|
if image_response_clicked && !self.image_zoomed && self.zoomed {
|
||||||
ctx.set_cursor_icon(egui::CursorIcon::PointingHand);
|
link_clicked = true;
|
||||||
}
|
*time_offset = time;
|
||||||
}
|
self.last_image_zoomed = image_response.id;
|
||||||
if text_response.clicked() && !self.image_zoomed {
|
self.image_zoomed = true;
|
||||||
let mouse_click = ui.input(|i| {
|
|
||||||
i.pointer.interact_pos().unwrap_or_default()
|
|
||||||
}) - text_rect.min.to_vec2();
|
|
||||||
|
|
||||||
editor.action(
|
|
||||||
self.font_system.lock().deref_mut(),
|
|
||||||
glyphon::Action::Click {
|
|
||||||
x: mouse_click.x as i32,
|
|
||||||
y: mouse_click.y as i32,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut location = editor.cursor();
|
|
||||||
match location.affinity {
|
|
||||||
glyphon::Affinity::After => location.index += 1,
|
|
||||||
glyphon::Affinity::Before => {}
|
|
||||||
}
|
|
||||||
if let Some(url) = url_map.get(location.index) {
|
|
||||||
link_clicked = true;
|
|
||||||
if url.starts_with('#') {
|
|
||||||
if let Some(icon) = CONTEXT_ICONS
|
|
||||||
.iter()
|
|
||||||
.find(|i| i.name == &url[1..])
|
|
||||||
{
|
|
||||||
icon_link = Some(icon);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ui.ctx().open_url(OpenUrl::new_tab(url));
|
|
||||||
}
|
|
||||||
// clicked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buffers.push(BufferWithTextArea::new(
|
|
||||||
buffer.clone(),
|
|
||||||
text_rect,
|
|
||||||
zoom_view_opacity,
|
|
||||||
Color::rgb(255, 255, 255),
|
|
||||||
ui.ctx(),
|
|
||||||
));
|
|
||||||
match context_block.1.modifier {
|
|
||||||
Some(ListKind::Unordered) => {
|
|
||||||
ui.painter().circle(
|
|
||||||
text_rect.min
|
|
||||||
+ Vec2::new(
|
|
||||||
-INDENT_AMOUNT,
|
|
||||||
CONTEXT_METRICS.line_height / 2.0,
|
|
||||||
),
|
|
||||||
2.5,
|
|
||||||
Color32::WHITE.gamma_multiply(zoom_view_opacity),
|
|
||||||
Stroke::NONE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*time_offset
|
||||||
|
});
|
||||||
|
|
||||||
|
if image_response.hovered() && !self.image_zoomed {
|
||||||
|
ui.ctx().set_cursor_icon(egui::CursorIcon::ZoomIn);
|
||||||
}
|
}
|
||||||
ContextBlock::Image {
|
|
||||||
image, hi_image, ..
|
|
||||||
} => {
|
|
||||||
let image_rect = context_block.0.translate(rect.min.to_vec2());
|
|
||||||
|
|
||||||
let image_response = ui.allocate_rect(image_rect, Sense::click());
|
let fs_rect = Align2::CENTER_CENTER.align_size_within_rect(
|
||||||
|
{
|
||||||
let time_offset = ui.memory_mut(|m| {
|
let max_size = self.max_size.shrink(16.0).size();
|
||||||
let time_offset =
|
ImageSize {
|
||||||
m.data.get_temp_mut_or_default::<f64>(image_response.id);
|
max_size,
|
||||||
|
..Default::default()
|
||||||
if image_response.clicked() && !self.image_zoomed && self.zoomed
|
|
||||||
{
|
|
||||||
link_clicked = true;
|
|
||||||
*time_offset = time;
|
|
||||||
self.last_image_zoomed = image_response.id;
|
|
||||||
self.image_zoomed = true;
|
|
||||||
}
|
}
|
||||||
|
.calc_size(max_size, image_rect.size())
|
||||||
|
},
|
||||||
|
self.max_size,
|
||||||
|
);
|
||||||
|
|
||||||
*time_offset
|
if self.last_image_zoomed == image_response.id {
|
||||||
});
|
let ui = egui::Ui::new(
|
||||||
|
ui.ctx().clone(),
|
||||||
if image_response.hovered() && !self.image_zoomed {
|
LayerId::debug(),
|
||||||
ui.ctx().set_cursor_icon(egui::CursorIcon::ZoomIn);
|
Id::new("image_zoom"),
|
||||||
}
|
self.max_size,
|
||||||
|
|
||||||
let fs_rect = Align2::CENTER_CENTER.align_size_within_rect(
|
|
||||||
{
|
|
||||||
let max_size = self.max_size.shrink(16.0).size();
|
|
||||||
ImageSize {
|
|
||||||
max_size,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.calc_size(max_size, image_rect.size())
|
|
||||||
},
|
|
||||||
self.max_size,
|
self.max_size,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.last_image_zoomed == image_response.id {
|
let t = keyframe::ease_with_scaled_time(
|
||||||
let ui = egui::Ui::new(
|
functions::EaseInOutCubic,
|
||||||
ui.ctx().clone(),
|
if self.image_zoomed { 0.0 } else { 1.0 },
|
||||||
LayerId::debug(),
|
if self.image_zoomed { 1.0 } else { 0.0 },
|
||||||
Id::new("image_zoom"),
|
time - time_offset,
|
||||||
self.max_size,
|
0.5,
|
||||||
self.max_size,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
let t = keyframe::ease_with_scaled_time(
|
ui.painter().rect_filled(
|
||||||
|
ui.max_rect(),
|
||||||
|
Rounding::default(),
|
||||||
|
Color32::BLACK.gamma_multiply(keyframe::ease_with_scaled_time(
|
||||||
functions::EaseInOutCubic,
|
functions::EaseInOutCubic,
|
||||||
if self.image_zoomed { 0.0 } else { 1.0 },
|
if self.image_zoomed { 0.0 } else { 0.6 },
|
||||||
if self.image_zoomed { 1.0 } else { 0.0 },
|
if self.image_zoomed { 0.6 } else { 0.0 },
|
||||||
time - time_offset,
|
time - time_offset,
|
||||||
0.5,
|
0.5,
|
||||||
);
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
ui.painter().rect_filled(
|
if t > 0.0 {
|
||||||
ui.max_rect(),
|
Image::from_uri(&image_urls.1)
|
||||||
Rounding::default(),
|
} else {
|
||||||
Color32::BLACK.gamma_multiply(
|
Image::from_uri(&image_urls.0)
|
||||||
|
}
|
||||||
|
.tint(Color32::WHITE.gamma_multiply(zoom_view_opacity))
|
||||||
|
.paint_at(&ui, image_rect.lerp_towards(&fs_rect, t));
|
||||||
|
} else {
|
||||||
|
Image::from_uri(&image_urls.0)
|
||||||
|
.tint(Color32::WHITE.gamma_multiply(zoom_view_opacity))
|
||||||
|
.paint_at(
|
||||||
|
ui,
|
||||||
|
image_rect.lerp_towards(
|
||||||
|
&fs_rect,
|
||||||
keyframe::ease_with_scaled_time(
|
keyframe::ease_with_scaled_time(
|
||||||
functions::EaseInOutCubic,
|
functions::EaseInOutCubic,
|
||||||
if self.image_zoomed { 0.0 } else { 0.6 },
|
1.0,
|
||||||
if self.image_zoomed { 0.6 } else { 0.0 },
|
0.0,
|
||||||
time - time_offset,
|
time - time_offset,
|
||||||
0.5,
|
0.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let text_rect =
|
||||||
|
context_block.relative_bounds.translate(rect.min.to_vec2());
|
||||||
|
// ui.painter().debug_rect(text_rect, Color32::GREEN, "");
|
||||||
|
if let Some(url_map) = &context_block.url_map {
|
||||||
|
let text_response = ui.allocate_rect(text_rect, Sense::click());
|
||||||
|
let mut buffer = context_block.buffer.write();
|
||||||
|
let mut editor =
|
||||||
|
Editor::new(BufferRef::Borrowed(buffer.deref_mut()));
|
||||||
|
if text_response.hovered()
|
||||||
|
&& ui.input(|i| i.raw_scroll_delta == Vec2::ZERO)
|
||||||
|
{
|
||||||
|
let mouse_pos = ui
|
||||||
|
.input(|i| i.pointer.latest_pos().unwrap_or_default())
|
||||||
|
- text_rect.min.to_vec2();
|
||||||
|
|
||||||
if t > 0.0 {
|
editor.action(
|
||||||
hi_image.clone()
|
self.font_system.lock().deref_mut(),
|
||||||
} else {
|
glyphon::Action::Click {
|
||||||
image.clone()
|
x: mouse_pos.x as i32,
|
||||||
|
y: mouse_pos.y as i32 - 3,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut location = editor.cursor();
|
||||||
|
match location.affinity {
|
||||||
|
glyphon::Affinity::After => location.index += 1,
|
||||||
|
glyphon::Affinity::Before => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if url_map.get(&location.index).is_some() {
|
||||||
|
ctx.set_cursor_icon(egui::CursorIcon::PointingHand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if text_response.clicked() && !self.image_zoomed {
|
||||||
|
let mouse_click = ui
|
||||||
|
.input(|i| i.pointer.interact_pos().unwrap_or_default())
|
||||||
|
- text_rect.min.to_vec2();
|
||||||
|
|
||||||
|
editor.action(
|
||||||
|
self.font_system.lock().deref_mut(),
|
||||||
|
glyphon::Action::Click {
|
||||||
|
x: mouse_click.x as i32,
|
||||||
|
y: mouse_click.y as i32,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut location = editor.cursor();
|
||||||
|
match location.affinity {
|
||||||
|
glyphon::Affinity::After => location.index += 1,
|
||||||
|
glyphon::Affinity::Before => {}
|
||||||
|
}
|
||||||
|
if let Some(url) = url_map.get(&location.index) {
|
||||||
|
link_clicked = true;
|
||||||
|
if url.starts_with('#') {
|
||||||
|
if let Some(icon) =
|
||||||
|
CONTEXT_ICONS.iter().find(|i| i.name == &url[1..])
|
||||||
|
{
|
||||||
|
icon_link = Some(icon);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ui.ctx().open_url(OpenUrl::new_tab(url));
|
||||||
|
}
|
||||||
|
// clicked = false;
|
||||||
}
|
}
|
||||||
.tint(Color32::WHITE.gamma_multiply(zoom_view_opacity))
|
|
||||||
.paint_at(&ui, image_rect.lerp_towards(&fs_rect, t));
|
|
||||||
} else {
|
|
||||||
image
|
|
||||||
.clone()
|
|
||||||
.tint(Color32::WHITE.gamma_multiply(zoom_view_opacity))
|
|
||||||
.paint_at(
|
|
||||||
ui,
|
|
||||||
image_rect.lerp_towards(
|
|
||||||
&fs_rect,
|
|
||||||
keyframe::ease_with_scaled_time(
|
|
||||||
functions::EaseInOutCubic,
|
|
||||||
1.0,
|
|
||||||
0.0,
|
|
||||||
time - time_offset,
|
|
||||||
0.5,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
buffers.push(BufferWithTextArea::new(
|
||||||
|
context_block.buffer.clone(),
|
||||||
|
text_rect,
|
||||||
|
zoom_view_opacity,
|
||||||
|
Color::rgb(255, 255, 255),
|
||||||
|
ui.ctx(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if self.zoomed {
|
if self.zoomed {
|
||||||
if ui.input(|i| i.pointer.any_click()) && !link_clicked {
|
if ui.input(|i| i.pointer.any_click()) && !link_clicked {
|
||||||
|
|
Loading…
Reference in a new issue