commit
289c318804
3 changed files with 279 additions and 0 deletions
@ -0,0 +1,31 @@ |
|||||||
|
//let sdl_context = sdl2::init()?;
|
||||||
|
//let video_subsys = sdl_context.video()?;
|
||||||
|
//let ttf_context = sdl2::ttf::init().map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
const GLYPH_METRICS_CAPACITY: usize = 128; |
||||||
|
|
||||||
|
struct GlyphMetric { |
||||||
|
ax: f32, // advance.x
|
||||||
|
ay: f32, // advance.y
|
||||||
|
|
||||||
|
bw: f32, // bitmap.width;
|
||||||
|
bh: f32, // bitmap.rows;
|
||||||
|
|
||||||
|
bl: f32, // bitmap_left;
|
||||||
|
bt: f32, // bitmap_top;
|
||||||
|
|
||||||
|
tx: f32, // x offset of glyph in texture coordinates
|
||||||
|
} |
||||||
|
|
||||||
|
struct FreeGlyphAtlas { |
||||||
|
atlas_width: usize, // TODO: Get FT_Uint from FT2build
|
||||||
|
atlas_height: usize, |
||||||
|
glyphs_texture: GLuint, |
||||||
|
metrics: [GlyphMetric; GLYPH_METRICS_CAPACITY], |
||||||
|
} |
||||||
|
|
||||||
|
fn free_glyph_atlas_init(atlas: FreeGlyphAtlas, face: FT_Face) { |
||||||
|
for i in 32..127 { |
||||||
|
// something something sdl2::ttf::GlyphMetrics
|
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,14 @@ |
|||||||
|
use std::fs; |
||||||
|
|
||||||
|
struct TypeFace { |
||||||
|
type_char: [[bool; 8]; 14], |
||||||
|
} |
||||||
|
|
||||||
|
fn get_font() { |
||||||
|
let file_path = "./fonts/Terminus14x8.data"; |
||||||
|
|
||||||
|
println!("In file {}", file_path); |
||||||
|
|
||||||
|
let contents = fs::read_to_string(file_path) |
||||||
|
.expect("File could not be read"); |
||||||
|
} |
||||||
@ -0,0 +1,234 @@ |
|||||||
|
extern crate sdl2; |
||||||
|
|
||||||
|
use std::path::Path; |
||||||
|
|
||||||
|
use sdl2::event::Event; |
||||||
|
use sdl2::keyboard::Keycode; |
||||||
|
use sdl2::pixels::Color; |
||||||
|
use sdl2::rect::Rect; |
||||||
|
use sdl2::render::TextureQuery; |
||||||
|
|
||||||
|
//mod _catlas;
|
||||||
|
//use _catlas::*;
|
||||||
|
|
||||||
|
mod font_build; |
||||||
|
use font_build::*; |
||||||
|
|
||||||
|
static SCREEN_WIDTH: u32 = 1680; |
||||||
|
static SCREEN_HEIGHT: u32 = 945; |
||||||
|
|
||||||
|
struct ModifierKeys { |
||||||
|
alt: bool, |
||||||
|
ctrl: bool, |
||||||
|
shift: bool, |
||||||
|
} |
||||||
|
|
||||||
|
// handle the annoying Rect i32
|
||||||
|
macro_rules! rect( |
||||||
|
($x:expr, $y:expr, $w:expr, $h:expr) => ( |
||||||
|
Rect::new($x as i32, $y as i32, $w as u32, $h as u32) |
||||||
|
) |
||||||
|
); |
||||||
|
|
||||||
|
// Scale fonts to a reasonable size when they're too big (though they might look less smooth)
|
||||||
|
/* |
||||||
|
fn get_centered_rect(rect_width: u32, rect_height: u32, cons_width: u32, cons_height: u32) -> Rect { |
||||||
|
let wr = rect_width as f32 / cons_width as f32; |
||||||
|
let hr = rect_height as f32 / cons_height as f32; |
||||||
|
|
||||||
|
let (w, h) = if wr > 1f32 || hr > 1f32 { |
||||||
|
if wr > hr { |
||||||
|
println!("Scaling down! The text will look worse!"); |
||||||
|
let h = (rect_height as f32 / wr) as i32; |
||||||
|
(cons_width as i32, h) |
||||||
|
} else { |
||||||
|
println!("Scaling down! The text will look worse!"); |
||||||
|
let w = (rect_width as f32 / hr) as i32; |
||||||
|
(w, cons_height as i32) |
||||||
|
} |
||||||
|
} else { |
||||||
|
(rect_width as i32, rect_height as i32) |
||||||
|
}; |
||||||
|
|
||||||
|
let cx = (SCREEN_WIDTH as i32 - w) / 2; |
||||||
|
let cy = (SCREEN_HEIGHT as i32 - h) / 2; |
||||||
|
rect!(cx, cy, w, h) |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
fn get_corner_rect(rect_width: u32, rect_height: u32) -> Rect { |
||||||
|
let (w,h) = (rect_width as i32, rect_height as i32); |
||||||
|
let cx = 15; |
||||||
|
let cy = 15; |
||||||
|
rect!(cx, cy, w, h) |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
pub fn main() -> Result<(), String> { |
||||||
|
let font_path: &Path = Path::new("./fonts/Monoid-Regular.ttf"); |
||||||
|
|
||||||
|
let sdl_context = sdl2::init()?; |
||||||
|
let video_subsys = sdl_context.video()?; |
||||||
|
let ttf_context = sdl2::ttf::init().map_err(|e| e.to_string())?; |
||||||
|
|
||||||
|
let window = video_subsys |
||||||
|
.window("SDL2_TTF Example", SCREEN_WIDTH, SCREEN_HEIGHT) |
||||||
|
.position_centered() |
||||||
|
.opengl() |
||||||
|
.build() |
||||||
|
.map_err(|e| e.to_string())?; |
||||||
|
|
||||||
|
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?; |
||||||
|
let texture_creator = canvas.texture_creator(); |
||||||
|
|
||||||
|
// Load a font
|
||||||
|
let mut font = ttf_context.load_font(font_path, 12)?; |
||||||
|
font.set_style(sdl2::ttf::FontStyle::BOLD); |
||||||
|
|
||||||
|
let mut buffer = String::new(); |
||||||
|
let mut cursor_position = 0; |
||||||
|
|
||||||
|
let mut draw_text = |text: &str| -> Result<(), String> { |
||||||
|
// render a surface, and convert it to a texture bound to the canvas
|
||||||
|
let surface = font |
||||||
|
.render(text) |
||||||
|
.blended(Color::RGBA(255, 255, 255, 255)) |
||||||
|
.map_err(|e| e.to_string())?; |
||||||
|
let texture = texture_creator |
||||||
|
.create_texture_from_surface(&surface) |
||||||
|
.map_err(|e| e.to_string())?; |
||||||
|
|
||||||
|
canvas.set_draw_color(Color::RGB(32, 32, 32)); |
||||||
|
canvas.clear(); |
||||||
|
|
||||||
|
let TextureQuery { width, height, .. } = texture.query(); |
||||||
|
|
||||||
|
// If the example text is too big for the screen, downscale it (and center regardless)
|
||||||
|
// let padding = 64;
|
||||||
|
let target = get_corner_rect( |
||||||
|
width, |
||||||
|
height, |
||||||
|
); |
||||||
|
|
||||||
|
canvas.copy(&texture, None, Some(target))?; |
||||||
|
canvas.present(); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
}; |
||||||
|
|
||||||
|
let mut modifier_keys = ModifierKeys {alt: false, ctrl: false, shift: false}; |
||||||
|
|
||||||
|
'mainloop: loop { |
||||||
|
for event in sdl_context.event_pump()?.poll_iter() { |
||||||
|
match event { |
||||||
|
// ESC or SIGKILL
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Escape), .. } | |
||||||
|
Event::Quit { .. } => break 'mainloop, |
||||||
|
|
||||||
|
// ALT down
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::LAlt), .. } | |
||||||
|
Event::KeyDown { keycode: Some(Keycode::RAlt), .. } => { |
||||||
|
modifier_keys.alt = true |
||||||
|
}, |
||||||
|
|
||||||
|
// ALT up
|
||||||
|
Event::KeyUp { keycode: Some(Keycode::LAlt), .. } | |
||||||
|
Event::KeyUp { keycode: Some(Keycode::RAlt), .. } => { |
||||||
|
modifier_keys.alt = false |
||||||
|
}, |
||||||
|
|
||||||
|
// CTRL down
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::LCtrl), .. } | |
||||||
|
Event::KeyDown { keycode: Some(Keycode::RCtrl), .. } => { |
||||||
|
modifier_keys.ctrl = true |
||||||
|
}, |
||||||
|
|
||||||
|
// CTRL up
|
||||||
|
Event::KeyUp { keycode: Some(Keycode::LCtrl), .. } | |
||||||
|
Event::KeyUp { keycode: Some(Keycode::RCtrl), .. } => { |
||||||
|
modifier_keys.ctrl = false |
||||||
|
}, |
||||||
|
|
||||||
|
// SHIFT down
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::LShift), .. } | |
||||||
|
Event::KeyDown { keycode: Some(Keycode::RShift), .. } => { |
||||||
|
modifier_keys.shift = true |
||||||
|
}, |
||||||
|
|
||||||
|
// SHIFT up
|
||||||
|
Event::KeyUp { keycode: Some(Keycode::LShift), .. } | |
||||||
|
Event::KeyUp { keycode: Some(Keycode::RShift), .. } => { |
||||||
|
modifier_keys.shift = false |
||||||
|
}, |
||||||
|
|
||||||
|
// Ignore SUPER
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::LGui), .. } | |
||||||
|
Event::KeyDown { keycode: Some(Keycode::RGui), .. } => (), |
||||||
|
|
||||||
|
// Type spacebar
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Space), .. } => { |
||||||
|
buffer.insert(cursor_position, ' '); |
||||||
|
cursor_position += 1; |
||||||
|
draw_text(&buffer)? |
||||||
|
}, |
||||||
|
|
||||||
|
// Backspace
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Backspace), .. } => { |
||||||
|
buffer.remove(cursor_position - 1); |
||||||
|
cursor_position -= 1; |
||||||
|
draw_text(&buffer)? |
||||||
|
}, |
||||||
|
|
||||||
|
// Enter
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Return), .. } => { |
||||||
|
let key = '\n'; |
||||||
|
buffer.insert(cursor_position, key); |
||||||
|
cursor_position += 1; |
||||||
|
draw_text(&buffer)? |
||||||
|
}, |
||||||
|
|
||||||
|
// Left/Back arrow
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Left), .. } => { |
||||||
|
cursor_position = usize::checked_sub(cursor_position, 1) |
||||||
|
.unwrap_or(0) |
||||||
|
}, |
||||||
|
|
||||||
|
// Home key
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Home), .. } => { |
||||||
|
cursor_position = 0 |
||||||
|
}, |
||||||
|
|
||||||
|
// End key
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::End), .. } => { |
||||||
|
cursor_position = buffer.len() |
||||||
|
}, |
||||||
|
|
||||||
|
// Right/Forward arrow
|
||||||
|
Event::KeyDown { keycode: Some(Keycode::Right), .. } => { |
||||||
|
cursor_position = (cursor_position + 1).min(buffer.len()) |
||||||
|
}, |
||||||
|
|
||||||
|
Event::KeyDown { keycode, .. } => { |
||||||
|
let key = keycode.unwrap().to_string(); |
||||||
|
|
||||||
|
// Ignore multi-char keycodes
|
||||||
|
if key.len() > 1 { break } |
||||||
|
|
||||||
|
let key_case = match modifier_keys.shift { |
||||||
|
true => key.to_uppercase(), |
||||||
|
false => key.to_lowercase() |
||||||
|
}.chars().nth(0).expect("Empty"); |
||||||
|
|
||||||
|
buffer.insert(cursor_position, key_case); |
||||||
|
cursor_position += 1; |
||||||
|
draw_text(&buffer)?; |
||||||
|
println!("{key}"); |
||||||
|
}, |
||||||
|
|
||||||
|
_ => {} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
Loading…
Reference in new issue