feat: add kraft paper tiled background texture for comfortable reading

This commit is contained in:
Developer
2026-05-14 20:49:27 +08:00
parent 16f801cdf8
commit d69229b1ca
5 changed files with 93 additions and 0 deletions

View File

@@ -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,

View File

@@ -5,6 +5,7 @@ mod book;
mod font;
mod persistence;
mod reader;
mod texture;
mod theme;
fn main() -> eframe::Result {

View File

@@ -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
View 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;
}
}

View File

@@ -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(),