feat: add kraft paper tiled background texture for comfortable reading
This commit is contained in:
@@ -8,6 +8,7 @@ pub struct App {
|
||||
pub state: AppState,
|
||||
settings: Settings,
|
||||
settings_dir: std::path::PathBuf,
|
||||
kraft_texture: Option<egui::TextureHandle>,
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
@@ -51,6 +52,7 @@ impl App {
|
||||
let settings_dir = persistence::settings_dir();
|
||||
let settings = persistence::load_settings(&settings_dir).unwrap_or_default();
|
||||
cc.egui_ctx.set_style(theme::create_style(&settings.theme));
|
||||
let kraft_texture = Some(crate::texture::generate_kraft_paper(&cc.egui_ctx));
|
||||
Self {
|
||||
state: AppState {
|
||||
book: None,
|
||||
@@ -62,6 +64,7 @@ impl App {
|
||||
},
|
||||
settings,
|
||||
settings_dir,
|
||||
kraft_texture,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +192,8 @@ impl eframe::App for App {
|
||||
&mut self.settings.font_size,
|
||||
&self.settings.theme,
|
||||
&file_path,
|
||||
self.kraft_texture.as_ref(),
|
||||
self.settings.use_kraft_bg,
|
||||
);
|
||||
|
||||
if action.go_back {
|
||||
@@ -198,6 +203,10 @@ impl eframe::App for App {
|
||||
self.state.current_page = 0;
|
||||
}
|
||||
|
||||
if action.toggle_kraft_bg {
|
||||
self.settings.use_kraft_bg = !self.settings.use_kraft_bg;
|
||||
}
|
||||
|
||||
if action.toggle_theme {
|
||||
self.settings.theme = match self.settings.theme {
|
||||
theme::Theme::Light => theme::Theme::Dark,
|
||||
|
||||
@@ -5,6 +5,7 @@ mod book;
|
||||
mod font;
|
||||
mod persistence;
|
||||
mod reader;
|
||||
mod texture;
|
||||
mod theme;
|
||||
|
||||
fn main() -> eframe::Result {
|
||||
|
||||
@@ -25,6 +25,7 @@ pub struct ReaderAction {
|
||||
pub go_back: bool,
|
||||
pub toggle_theme: bool,
|
||||
pub toggle_bookmark: bool,
|
||||
pub toggle_kraft_bg: bool,
|
||||
pub page_next: bool,
|
||||
pub page_prev: bool,
|
||||
}
|
||||
@@ -38,11 +39,14 @@ pub fn reading_view(
|
||||
font_size: &mut f32,
|
||||
theme: &Theme,
|
||||
_file_path: &str,
|
||||
kraft_texture: Option<&egui::TextureHandle>,
|
||||
use_kraft_bg: bool,
|
||||
) -> ReaderAction {
|
||||
let mut action = ReaderAction {
|
||||
go_back: false,
|
||||
toggle_theme: false,
|
||||
toggle_bookmark: false,
|
||||
toggle_kraft_bg: false,
|
||||
page_next: false,
|
||||
page_prev: false,
|
||||
};
|
||||
@@ -91,6 +95,10 @@ pub fn reading_view(
|
||||
if ui.button("A⁺").on_hover_text("放大字体").clicked() {
|
||||
*font_size = (*font_size + 2.0).min(48.0);
|
||||
}
|
||||
let kraft_icon = if use_kraft_bg { "📄" } else { "📋" };
|
||||
if ui.button(kraft_icon).on_hover_text("牛皮纸背景").clicked() {
|
||||
action.toggle_kraft_bg = true;
|
||||
}
|
||||
if ui.button("☰").on_hover_text("打开/关闭目录").clicked() {
|
||||
*sidebar_open = !*sidebar_open;
|
||||
}
|
||||
@@ -163,6 +171,13 @@ pub fn reading_view(
|
||||
let available = ui.available_size();
|
||||
let (rect, response) = ui.allocate_at_least(available, egui::Sense::click());
|
||||
|
||||
// Draw kraft paper background if enabled
|
||||
if use_kraft_bg {
|
||||
if let Some(tex) = kraft_texture {
|
||||
crate::texture::draw_tiled_bg(ui.painter(), rect, tex);
|
||||
}
|
||||
}
|
||||
|
||||
// Add reading margins (inset)
|
||||
let inset = 24.0;
|
||||
let text_rect = egui::Rect::from_min_size(
|
||||
|
||||
66
src/texture.rs
Normal file
66
src/texture.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use eframe::egui;
|
||||
|
||||
fn hash(x: u32, y: u32) -> u32 {
|
||||
let mut h = x.wrapping_mul(374761393) ^ y.wrapping_mul(668265263);
|
||||
h = h.wrapping_mul(h ^ (h >> 13));
|
||||
h ^ (h >> 15)
|
||||
}
|
||||
|
||||
fn paper_noise(x: u32, y: u32) -> u8 {
|
||||
// Combine multiple hash values for more natural-looking noise
|
||||
let n1 = hash(x, y);
|
||||
let n2 = hash(x.wrapping_add(137), y.wrapping_add(251));
|
||||
let n3 = hash(x.wrapping_mul(7), y.wrapping_mul(13));
|
||||
((n1 ^ n2.wrapping_add(n3)) % 18) as u8
|
||||
}
|
||||
|
||||
pub fn generate_kraft_paper(ctx: &egui::Context) -> egui::TextureHandle {
|
||||
let size = 128usize;
|
||||
let mut pixels = Vec::with_capacity(size * size);
|
||||
|
||||
for y in 0..size {
|
||||
for x in 0..size {
|
||||
let n = paper_noise(x as u32, y as u32);
|
||||
// Warm brown-beige base with slight variation
|
||||
let r = 212u8.wrapping_add(n);
|
||||
let g = 194u8.wrapping_add(n);
|
||||
let b = 168u8.wrapping_add(n);
|
||||
pixels.push(egui::Color32::from_rgb(r, g, b));
|
||||
}
|
||||
}
|
||||
|
||||
let image = egui::ColorImage {
|
||||
size: [size, size],
|
||||
pixels,
|
||||
};
|
||||
ctx.load_texture("kraft_paper", image, Default::default())
|
||||
}
|
||||
|
||||
pub fn draw_tiled_bg(
|
||||
painter: &egui::Painter,
|
||||
rect: egui::Rect,
|
||||
texture: &egui::TextureHandle,
|
||||
) {
|
||||
let tile_size = 128.0f32;
|
||||
let mut ty = rect.min.y;
|
||||
while ty < rect.max.y {
|
||||
let mut tx = rect.min.x;
|
||||
while tx < rect.max.x {
|
||||
let tile_rect = egui::Rect::from_min_max(
|
||||
egui::pos2(tx, ty),
|
||||
egui::pos2(
|
||||
(tx + tile_size).min(rect.max.x),
|
||||
(ty + tile_size).min(rect.max.y),
|
||||
),
|
||||
);
|
||||
painter.image(
|
||||
texture.id(),
|
||||
tile_rect,
|
||||
egui::Rect::from_min_max(egui::pos2(0.0, 0.0), egui::pos2(1.0, 1.0)),
|
||||
egui::Color32::WHITE,
|
||||
);
|
||||
tx += tile_size;
|
||||
}
|
||||
ty += tile_size;
|
||||
}
|
||||
}
|
||||
@@ -170,6 +170,7 @@ pub fn create_style(theme: &Theme) -> Style {
|
||||
pub struct Settings {
|
||||
pub font_size: f32,
|
||||
pub theme: Theme,
|
||||
pub use_kraft_bg: bool,
|
||||
pub recent_files: Vec<String>,
|
||||
pub reading_positions: std::collections::HashMap<String, ReadingPosition>,
|
||||
pub bookmarks: std::collections::HashMap<String, Vec<Bookmark>>,
|
||||
@@ -181,6 +182,7 @@ impl Default for Settings {
|
||||
Self {
|
||||
font_size: 20.0,
|
||||
theme: Theme::Light,
|
||||
use_kraft_bg: false,
|
||||
recent_files: Vec::new(),
|
||||
reading_positions: std::collections::HashMap::new(),
|
||||
bookmarks: std::collections::HashMap::new(),
|
||||
|
||||
Reference in New Issue
Block a user