mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-22 19:08:37 +00:00
Pull font API and associated refactoring from 0.1-dev
This commit is contained in:
parent
f60c597d11
commit
cebe02cb11
@ -1,16 +0,0 @@
|
|||||||
M+ FONTS Copyright (C) 2002-2017 M+ FONTS PROJECT
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
LICENSE_E
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
These fonts are free software.
|
|
||||||
Unlimited permission is granted to use, copy, and distribute them, with
|
|
||||||
or without modification, either commercially or noncommercially.
|
|
||||||
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
|
|
||||||
|
|
||||||
|
|
||||||
http://mplus-fonts.osdn.jp
|
|
||||||
@ -2,8 +2,6 @@ use imgui::*;
|
|||||||
|
|
||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
example: i32,
|
example: i32,
|
||||||
notify_text: &'static str,
|
notify_text: &'static str,
|
||||||
@ -25,15 +23,15 @@ impl Default for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let system = support::init(file!());
|
||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
support::run("color_button.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
system.main_loop(|_, ui| {
|
||||||
example_selector(&mut state, ui);
|
example_selector(&mut state, ui);
|
||||||
match state.example {
|
match state.example {
|
||||||
1 => example_1(&mut state, ui),
|
1 => example_1(&mut state, ui),
|
||||||
2 => example_2(ui),
|
2 => example_2(ui),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
true
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,6 @@ use image::{jpeg::JPEGDecoder, ImageDecoder};
|
|||||||
use imgui::*;
|
use imgui::*;
|
||||||
|
|
||||||
mod support;
|
mod support;
|
||||||
use self::support::Textures;
|
|
||||||
|
|
||||||
const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct CustomTexturesApp {
|
struct CustomTexturesApp {
|
||||||
@ -31,7 +28,7 @@ impl CustomTexturesApp {
|
|||||||
fn register_textures<F>(
|
fn register_textures<F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
gl_ctx: &F,
|
gl_ctx: &F,
|
||||||
textures: &mut Textures,
|
textures: &mut Textures<Rc<Texture2d>>,
|
||||||
) -> Result<(), Box<dyn Error>>
|
) -> Result<(), Box<dyn Error>>
|
||||||
where
|
where
|
||||||
F: Facade,
|
F: Facade,
|
||||||
@ -89,7 +86,7 @@ impl CustomTexturesApp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Lenna {
|
impl Lenna {
|
||||||
fn new<F>(gl_ctx: &F, textures: &mut Textures) -> Result<Self, Box<dyn Error>>
|
fn new<F>(gl_ctx: &F, textures: &mut Textures<Rc<Texture2d>>) -> Result<Self, Box<dyn Error>>
|
||||||
where
|
where
|
||||||
F: Facade,
|
F: Facade,
|
||||||
{
|
{
|
||||||
@ -121,16 +118,9 @@ impl Lenna {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let mut my_app = CustomTexturesApp::default();
|
let mut my_app = CustomTexturesApp::default();
|
||||||
|
|
||||||
support::run(
|
let mut system = support::init(file!());
|
||||||
"custom_textures.rs".to_owned(),
|
my_app
|
||||||
CLEAR_COLOR,
|
.register_textures(system.display.get_context(), system.renderer.textures())
|
||||||
|ui, gl_ctx, textures| {
|
.expect("Failed to register textures");
|
||||||
if let Err(e) = my_app.register_textures(gl_ctx, textures) {
|
system.main_loop(|_, ui| my_app.show_textures(ui));
|
||||||
panic!("Failed to register textures! {}", e);
|
|
||||||
}
|
|
||||||
my_app.show_textures(ui);
|
|
||||||
|
|
||||||
true
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,29 +2,21 @@ use imgui::*;
|
|||||||
|
|
||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
support::run("hello_world.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
let system = support::init(file!());
|
||||||
hello_world(ui)
|
system.main_loop(|_, ui| {
|
||||||
|
ui.window(im_str!("Hello world"))
|
||||||
|
.size([300.0, 100.0], Condition::FirstUseEver)
|
||||||
|
.build(|| {
|
||||||
|
ui.text(im_str!("Hello world!"));
|
||||||
|
ui.text(im_str!("こんにちは世界!"));
|
||||||
|
ui.text(im_str!("This...is...imgui-rs!"));
|
||||||
|
ui.separator();
|
||||||
|
let mouse_pos = ui.io().mouse_pos;
|
||||||
|
ui.text(format!(
|
||||||
|
"Mouse Position: ({:.1},{:.1})",
|
||||||
|
mouse_pos[0], mouse_pos[1]
|
||||||
|
));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hello_world<'a>(ui: &Ui<'a>) -> bool {
|
|
||||||
ui.window(im_str!("Hello world"))
|
|
||||||
.size([300.0, 100.0], Condition::FirstUseEver)
|
|
||||||
.build(|| {
|
|
||||||
ui.text(im_str!("Hello world!"));
|
|
||||||
ui.text(im_str!("こんにちは世界!"));
|
|
||||||
ui.text(im_str!("This...is...imgui-rs!"));
|
|
||||||
ui.separator();
|
|
||||||
let mouse_pos = ui.io().mouse_pos;
|
|
||||||
ui.text(im_str!(
|
|
||||||
"Mouse Position: ({:.1},{:.1})",
|
|
||||||
mouse_pos[0],
|
|
||||||
mouse_pos[1]
|
|
||||||
));
|
|
||||||
});
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,103 +1,112 @@
|
|||||||
use glium::{
|
use glium::glutin::{self, Event, WindowEvent};
|
||||||
backend::{Context, Facade},
|
use glium::{Display, Surface};
|
||||||
Texture2d,
|
use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui};
|
||||||
};
|
use imgui_glium_renderer::GliumRenderer;
|
||||||
use imgui::{self, FontGlyphRange, ImFontConfig, Ui};
|
|
||||||
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||||
use std::rc::Rc;
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
pub type Textures = imgui::Textures<Rc<Texture2d>>;
|
pub struct System {
|
||||||
|
pub events_loop: glutin::EventsLoop,
|
||||||
|
pub display: glium::Display,
|
||||||
|
pub imgui: Context,
|
||||||
|
pub platform: WinitPlatform,
|
||||||
|
pub renderer: GliumRenderer,
|
||||||
|
pub font_size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run<F>(title: String, clear_color: [f32; 4], mut run_ui: F)
|
pub fn init(title: &str) -> System {
|
||||||
where
|
let events_loop = glutin::EventsLoop::new();
|
||||||
F: FnMut(&Ui, &Rc<Context>, &mut Textures) -> bool,
|
|
||||||
{
|
|
||||||
use glium::glutin;
|
|
||||||
use glium::{Display, Surface};
|
|
||||||
use imgui_glium_renderer::GliumRenderer;
|
|
||||||
|
|
||||||
let mut events_loop = glutin::EventsLoop::new();
|
|
||||||
let context = glutin::ContextBuilder::new().with_vsync(true);
|
let context = glutin::ContextBuilder::new().with_vsync(true);
|
||||||
let builder = glutin::WindowBuilder::new()
|
let builder = glutin::WindowBuilder::new()
|
||||||
.with_title(title)
|
.with_title(title.to_owned())
|
||||||
.with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64));
|
.with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64));
|
||||||
let display = Display::new(builder, context, &events_loop).unwrap();
|
let display =
|
||||||
let gl_window = display.gl_window();
|
Display::new(builder, context, &events_loop).expect("Failed to initialize display");
|
||||||
let window = gl_window.window();
|
|
||||||
|
|
||||||
let mut imgui = imgui::Context::create();
|
let mut imgui = Context::create();
|
||||||
imgui.set_ini_filename(None);
|
imgui.set_ini_filename(None);
|
||||||
|
|
||||||
let mut platform = WinitPlatform::init(&mut imgui);
|
let mut platform = WinitPlatform::init(&mut imgui);
|
||||||
platform.attach_window(imgui.io_mut(), &window, HiDpiMode::Rounded);
|
{
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
let window = gl_window.window();
|
||||||
|
platform.attach_window(imgui.io_mut(), &window, HiDpiMode::Rounded);
|
||||||
|
}
|
||||||
|
|
||||||
let hidpi_factor = platform.hidpi_factor();
|
let hidpi_factor = platform.hidpi_factor();
|
||||||
let font_size = (13.0 * hidpi_factor) as f32;
|
let font_size = (13.0 * hidpi_factor) as f32;
|
||||||
|
imgui.fonts().add_font(&[
|
||||||
imgui.fonts().add_default_font_with_config(
|
FontSource::DefaultFontData {
|
||||||
ImFontConfig::new()
|
config: Some(FontConfig {
|
||||||
.oversample_h(1)
|
size_pixels: font_size,
|
||||||
.pixel_snap_h(true)
|
..FontConfig::default()
|
||||||
.size_pixels(font_size),
|
}),
|
||||||
);
|
},
|
||||||
|
FontSource::TtfData {
|
||||||
imgui.fonts().add_font_with_config(
|
data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
||||||
include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
size_pixels: font_size,
|
||||||
ImFontConfig::new()
|
config: Some(FontConfig {
|
||||||
.merge_mode(true)
|
rasterizer_multiply: 1.75,
|
||||||
.oversample_h(1)
|
glyph_ranges: FontGlyphRanges::japanese(),
|
||||||
.pixel_snap_h(true)
|
..FontConfig::default()
|
||||||
.size_pixels(font_size)
|
}),
|
||||||
.rasterizer_multiply(1.75),
|
},
|
||||||
&FontGlyphRange::japanese(),
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
||||||
|
|
||||||
let mut renderer =
|
let renderer =
|
||||||
GliumRenderer::init(&mut imgui, &display).expect("Failed to initialize renderer");
|
GliumRenderer::init(&mut imgui, &display).expect("Failed to initialize renderer");
|
||||||
|
|
||||||
let mut last_frame = Instant::now();
|
System {
|
||||||
let mut quit = false;
|
events_loop,
|
||||||
|
display,
|
||||||
|
imgui,
|
||||||
|
platform,
|
||||||
|
renderer,
|
||||||
|
font_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
impl System {
|
||||||
events_loop.poll_events(|event| {
|
pub fn main_loop<F: FnMut(&mut bool, &mut Ui)>(self, mut run_ui: F) {
|
||||||
use glium::glutin::{Event, WindowEvent::CloseRequested};
|
let System {
|
||||||
|
mut events_loop,
|
||||||
|
display,
|
||||||
|
mut imgui,
|
||||||
|
mut platform,
|
||||||
|
mut renderer,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
let gl_window = display.gl_window();
|
||||||
|
let window = gl_window.window();
|
||||||
|
let mut last_frame = Instant::now();
|
||||||
|
let mut run = true;
|
||||||
|
|
||||||
platform.handle_event(imgui.io_mut(), &window, &event);
|
while run {
|
||||||
|
events_loop.poll_events(|event| {
|
||||||
|
platform.handle_event(imgui.io_mut(), &window, &event);
|
||||||
|
|
||||||
if let Event::WindowEvent { event, .. } = event {
|
if let Event::WindowEvent { event, .. } = event {
|
||||||
match event {
|
if let WindowEvent::CloseRequested = event {
|
||||||
CloseRequested => quit = true,
|
run = false;
|
||||||
_ => (),
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
let io = imgui.io_mut();
|
let io = imgui.io_mut();
|
||||||
platform
|
platform
|
||||||
.prepare_frame(io, &window)
|
.prepare_frame(io, &window)
|
||||||
.expect("Failed to start frame");
|
.expect("Failed to start frame");
|
||||||
last_frame = io.update_delta_time(last_frame);
|
last_frame = io.update_delta_time(last_frame);
|
||||||
let ui = imgui.frame();
|
let mut ui = imgui.frame();
|
||||||
if !run_ui(&ui, display.get_context(), renderer.textures()) {
|
run_ui(&mut run, &mut ui);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut target = display.draw();
|
let mut target = display.draw();
|
||||||
target.clear_color(
|
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
|
||||||
clear_color[0],
|
platform.prepare_render(&ui, &window);
|
||||||
clear_color[1],
|
renderer.render(&mut target, ui).expect("Rendering failed");
|
||||||
clear_color[2],
|
target.finish().expect("Failed to swap buffers");
|
||||||
clear_color[3],
|
|
||||||
);
|
|
||||||
platform.prepare_render(&ui, &window);
|
|
||||||
renderer.render(&mut target, ui).expect("Rendering failed");
|
|
||||||
target.finish().unwrap();
|
|
||||||
|
|
||||||
if quit {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,43 +1,38 @@
|
|||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
|
|
||||||
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
||||||
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
support::run(
|
let system = support::init(file!());
|
||||||
"test_drawing_channels_split".to_owned(),
|
system.main_loop(|_, ui| {
|
||||||
CLEAR_COLOR,
|
let draw_list = ui.get_window_draw_list();
|
||||||
|ui, _, _| {
|
// Will draw channel 0 first, then channel 1, whatever the order of
|
||||||
let draw_list = ui.get_window_draw_list();
|
// the calls in the code.
|
||||||
// Will draw channel 0 first, then channel 1, whatever the order of
|
//
|
||||||
// the calls in the code.
|
// Here, we draw a red line on channel 1 then a white circle on
|
||||||
//
|
// channel 0. As a result, the red line will always appear on top of
|
||||||
// Here, we draw a red line on channel 1 then a white circle on
|
// the white circle.
|
||||||
// channel 0. As a result, the red line will always appear on top of
|
draw_list.channels_split(2, |channels| {
|
||||||
// the white circle.
|
const RADIUS: f32 = 100.0;
|
||||||
draw_list.channels_split(2, |channels| {
|
let canvas_pos = ui.get_cursor_screen_pos();
|
||||||
const RADIUS: f32 = 100.0;
|
channels.set_current(1);
|
||||||
let canvas_pos = ui.get_cursor_screen_pos();
|
draw_list
|
||||||
channels.set_current(1);
|
.add_line(
|
||||||
draw_list
|
canvas_pos,
|
||||||
.add_line(
|
[canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS],
|
||||||
canvas_pos,
|
RED,
|
||||||
[canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS],
|
)
|
||||||
RED,
|
.thickness(5.0)
|
||||||
)
|
.build();
|
||||||
.thickness(5.0)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
channels.set_current(0);
|
channels.set_current(0);
|
||||||
let center = [canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS];
|
let center = [canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS];
|
||||||
draw_list
|
draw_list
|
||||||
.add_circle(center, RADIUS, WHITE)
|
.add_circle(center, RADIUS, WHITE)
|
||||||
.thickness(10.0)
|
.thickness(10.0)
|
||||||
.num_segments(50)
|
.num_segments(50)
|
||||||
.build();
|
.build();
|
||||||
});
|
});
|
||||||
true
|
});
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
support::run("test_window.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
let system = support::init(file!());
|
||||||
let mut open = true;
|
system.main_loop(|run, ui| ui.show_demo_window(run));
|
||||||
ui.show_demo_window(&mut open);
|
|
||||||
open
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -182,16 +182,11 @@ impl Default for CustomRenderingState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const CLEAR_COLOR: [f32; 4] = [114.0 / 255.0, 144.0 / 255.0, 154.0 / 255.0, 1.0];
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut state = State::default();
|
let mut state = State::default();
|
||||||
|
|
||||||
support::run("test_window.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
let system = support::init(file!());
|
||||||
let mut open = true;
|
system.main_loop(|run, ui| show_test_window(ui, &mut state, run));
|
||||||
show_test_window(ui, &mut state, &mut open);
|
|
||||||
open
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show_help_marker(ui: &Ui, desc: &str) {
|
fn show_help_marker(ui: &Ui, desc: &str) {
|
||||||
|
|||||||
@ -1,16 +0,0 @@
|
|||||||
M+ FONTS Copyright (C) 2002-2017 M+ FONTS PROJECT
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
LICENSE_E
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
These fonts are free software.
|
|
||||||
Unlimited permission is granted to use, copy, and distribute them, with
|
|
||||||
or without modification, either commercially or noncommercially.
|
|
||||||
THESE FONTS ARE PROVIDED "AS IS" WITHOUT WARRANTY.
|
|
||||||
|
|
||||||
|
|
||||||
http://mplus-fonts.osdn.jp
|
|
||||||
@ -1,15 +1,15 @@
|
|||||||
use gfx::Device;
|
use gfx::Device;
|
||||||
use glutin::{Event, WindowEvent};
|
use glutin::{Event, WindowEvent};
|
||||||
use imgui::{FontGlyphRange, ImFontConfig, Context, Ui};
|
use imgui::{FontGlyphRanges, FontConfig, FontSource, Context, Ui};
|
||||||
use imgui_gfx_renderer::{GfxRenderer, Shaders};
|
use imgui_gfx_renderer::{GfxRenderer, Shaders};
|
||||||
use imgui_winit_support::{WinitPlatform, HiDpiMode};
|
use imgui_winit_support::{WinitPlatform, HiDpiMode};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
type ColorFormat = gfx::format::Rgba8;
|
||||||
|
type DepthFormat = gfx::format::DepthStencil;
|
||||||
|
|
||||||
#[cfg(feature = "opengl")]
|
#[cfg(feature = "opengl")]
|
||||||
pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_ui: F) {
|
pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_ui: F) {
|
||||||
type ColorFormat = gfx::format::Rgba8;
|
|
||||||
type DepthFormat = gfx::format::DepthStencil;
|
|
||||||
|
|
||||||
let mut events_loop = glutin::EventsLoop::new();
|
let mut events_loop = glutin::EventsLoop::new();
|
||||||
let context = glutin::ContextBuilder::new().with_vsync(true);
|
let context = glutin::ContextBuilder::new().with_vsync(true);
|
||||||
let builder = glutin::WindowBuilder::new()
|
let builder = glutin::WindowBuilder::new()
|
||||||
@ -17,7 +17,7 @@ pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_
|
|||||||
.with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64));
|
.with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64));
|
||||||
let (windowed_context, mut device, mut factory, mut main_color, mut main_depth) =
|
let (windowed_context, mut device, mut factory, mut main_color, mut main_depth) =
|
||||||
gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop)
|
gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop)
|
||||||
.expect("Failed to initalize graphics");
|
.expect("Failed to initialize graphics");
|
||||||
let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
|
let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
|
||||||
|
|
||||||
let shaders = {
|
let shaders = {
|
||||||
@ -64,24 +64,23 @@ pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_
|
|||||||
|
|
||||||
let hidpi_factor = platform.hidpi_factor();
|
let hidpi_factor = platform.hidpi_factor();
|
||||||
let font_size = (13.0 * hidpi_factor) as f32;
|
let font_size = (13.0 * hidpi_factor) as f32;
|
||||||
|
imgui.fonts().add_font(&[
|
||||||
imgui.fonts().add_default_font_with_config(
|
FontSource::DefaultFontData {
|
||||||
ImFontConfig::new()
|
config: Some(FontConfig {
|
||||||
.oversample_h(1)
|
size_pixels: font_size,
|
||||||
.pixel_snap_h(true)
|
..FontConfig::default()
|
||||||
.size_pixels(font_size),
|
}),
|
||||||
);
|
},
|
||||||
|
FontSource::TtfData {
|
||||||
imgui.fonts().add_font_with_config(
|
data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
||||||
include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
size_pixels: font_size,
|
||||||
ImFontConfig::new()
|
config: Some(FontConfig {
|
||||||
.merge_mode(true)
|
rasterizer_multiply: 1.75,
|
||||||
.oversample_h(1)
|
glyph_ranges: FontGlyphRanges::japanese(),
|
||||||
.pixel_snap_h(true)
|
..FontConfig::default()
|
||||||
.size_pixels(font_size)
|
}),
|
||||||
.rasterizer_multiply(1.75),
|
},
|
||||||
&FontGlyphRange::japanese(),
|
]);
|
||||||
);
|
|
||||||
|
|
||||||
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32;
|
||||||
|
|
||||||
@ -134,9 +133,6 @@ pub fn run<F: FnMut(&Ui) -> bool>(title: String, clear_color: [f32; 4], mut run_
|
|||||||
use gfx_window_dxgi;
|
use gfx_window_dxgi;
|
||||||
use glutin;
|
use glutin;
|
||||||
|
|
||||||
type ColorFormat = gfx::format::Rgba8;
|
|
||||||
type DepthFormat = gfx::format::DepthStencil;
|
|
||||||
|
|
||||||
let mut events_loop = glutin::EventsLoop::new();
|
let mut events_loop = glutin::EventsLoop::new();
|
||||||
let window = glutin::WindowBuilder::new()
|
let window = glutin::WindowBuilder::new()
|
||||||
.with_title(title)
|
.with_title(title)
|
||||||
|
|||||||
@ -136,21 +136,7 @@ where
|
|||||||
gfx::memory::Usage::Dynamic,
|
gfx::memory::Usage::Dynamic,
|
||||||
Bind::empty(),
|
Bind::empty(),
|
||||||
)?;
|
)?;
|
||||||
let (_, texture) = ctx.prepare_texture(|handle| {
|
let font_texture = upload_font_texture(ctx.fonts(), factory)?;
|
||||||
factory.create_texture_immutable_u8::<gfx::format::Rgba8>(
|
|
||||||
gfx::texture::Kind::D2(
|
|
||||||
handle.width as u16,
|
|
||||||
handle.height as u16,
|
|
||||||
gfx::texture::AaMode::Single,
|
|
||||||
),
|
|
||||||
gfx::texture::Mipmap::Provided,
|
|
||||||
&[handle.pixels],
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
let sampler =
|
|
||||||
factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp));
|
|
||||||
ctx.set_font_texture_id(TextureId::from(usize::MAX));
|
|
||||||
|
|
||||||
let slice = Slice {
|
let slice = Slice {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
@ -167,12 +153,20 @@ where
|
|||||||
index_buffer,
|
index_buffer,
|
||||||
slice,
|
slice,
|
||||||
pso,
|
pso,
|
||||||
font_texture: (texture, sampler),
|
font_texture,
|
||||||
textures: Textures::new(),
|
textures: Textures::new(),
|
||||||
#[cfg(feature = "directx")]
|
#[cfg(feature = "directx")]
|
||||||
constants: factory.create_constant_buffer(1),
|
constants: factory.create_constant_buffer(1),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn reload_font_texture<F: Factory<R>>(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut imgui::Context,
|
||||||
|
factory: &mut F,
|
||||||
|
) -> Result<(), GfxRendererError> {
|
||||||
|
self.font_texture = upload_font_texture(ctx.fonts(), factory)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub fn textures(&mut self) -> &mut Textures<Texture<R>> {
|
pub fn textures(&mut self) -> &mut Textures<Texture<R>> {
|
||||||
&mut self.textures
|
&mut self.textures
|
||||||
}
|
}
|
||||||
@ -314,6 +308,26 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn upload_font_texture<R: Resources, F: Factory<R>>(
|
||||||
|
mut fonts: imgui::FontAtlasRefMut,
|
||||||
|
factory: &mut F,
|
||||||
|
) -> Result<Texture<R>, GfxRendererError> {
|
||||||
|
let texture = fonts.build_rgba32_texture();
|
||||||
|
let (_, texture_view) = factory.create_texture_immutable_u8::<gfx::format::Srgba8>(
|
||||||
|
gfx::texture::Kind::D2(
|
||||||
|
texture.width as u16,
|
||||||
|
texture.height as u16,
|
||||||
|
gfx::texture::AaMode::Single,
|
||||||
|
),
|
||||||
|
gfx::texture::Mipmap::Provided,
|
||||||
|
&[texture.data],
|
||||||
|
)?;
|
||||||
|
fonts.tex_id = TextureId::from(usize::MAX);
|
||||||
|
let sampler = factory.create_sampler(SamplerInfo::new(FilterMethod::Bilinear, WrapMode::Tile));
|
||||||
|
let font_texture = (texture_view, sampler);
|
||||||
|
Ok(font_texture)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "directx")]
|
#[cfg(feature = "directx")]
|
||||||
mod constants {
|
mod constants {
|
||||||
use gfx::gfx_constant_struct_meta;
|
use gfx::gfx_constant_struct_meta;
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use glium::backend::{Context, Facade};
|
use glium::backend::{Context, Facade};
|
||||||
use glium::index::{self, PrimitiveType};
|
use glium::index::{self, PrimitiveType};
|
||||||
use glium::program::ProgramChooserCreationError;
|
use glium::program::ProgramChooserCreationError;
|
||||||
use glium::texture::{ClientFormat, RawImage2d, TextureCreationError};
|
use glium::texture::{ClientFormat, MipmapsOption, RawImage2d, TextureCreationError};
|
||||||
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter};
|
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter};
|
||||||
use glium::{
|
use glium::{
|
||||||
program, uniform, vertex, Blend, DrawError, DrawParameters, IndexBuffer, Program, Rect,
|
program, uniform, vertex, Blend, DrawError, DrawParameters, IndexBuffer, Program, Rect,
|
||||||
@ -81,16 +81,7 @@ impl GliumRenderer {
|
|||||||
facade: &F,
|
facade: &F,
|
||||||
) -> Result<GliumRenderer, GliumRendererError> {
|
) -> Result<GliumRenderer, GliumRendererError> {
|
||||||
let program = compile_default_program(facade)?;
|
let program = compile_default_program(facade)?;
|
||||||
let font_texture = ctx.prepare_texture(|handle| {
|
let font_texture = upload_font_texture(ctx.fonts(), facade.get_context())?;
|
||||||
let data = RawImage2d {
|
|
||||||
data: Cow::Borrowed(handle.pixels),
|
|
||||||
width: handle.width,
|
|
||||||
height: handle.height,
|
|
||||||
format: ClientFormat::U8U8U8U8,
|
|
||||||
};
|
|
||||||
Texture2d::new(facade, data)
|
|
||||||
})?;
|
|
||||||
ctx.set_font_texture_id(TextureId::from(usize::MAX));
|
|
||||||
ctx.set_renderer_name(Some(ImString::from(format!(
|
ctx.set_renderer_name(Some(ImString::from(format!(
|
||||||
"imgui-glium-renderer {}",
|
"imgui-glium-renderer {}",
|
||||||
env!("CARGO_PKG_VERSION")
|
env!("CARGO_PKG_VERSION")
|
||||||
@ -102,6 +93,13 @@ impl GliumRenderer {
|
|||||||
textures: Textures::new(),
|
textures: Textures::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
pub fn reload_font_texture(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut imgui::Context,
|
||||||
|
) -> Result<(), GliumRendererError> {
|
||||||
|
self.font_texture = upload_font_texture(ctx.fonts(), &self.ctx)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
pub fn textures(&mut self) -> &mut Textures<Rc<Texture2d>> {
|
pub fn textures(&mut self) -> &mut Textures<Rc<Texture2d>> {
|
||||||
&mut self.textures
|
&mut self.textures
|
||||||
}
|
}
|
||||||
@ -212,6 +210,22 @@ impl GliumRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn upload_font_texture(
|
||||||
|
mut fonts: imgui::FontAtlasRefMut,
|
||||||
|
ctx: &Rc<Context>,
|
||||||
|
) -> Result<Texture2d, GliumRendererError> {
|
||||||
|
let texture = fonts.build_rgba32_texture();
|
||||||
|
let data = RawImage2d {
|
||||||
|
data: Cow::Borrowed(texture.data),
|
||||||
|
width: texture.width,
|
||||||
|
height: texture.height,
|
||||||
|
format: ClientFormat::U8U8U8U8,
|
||||||
|
};
|
||||||
|
let font_texture = Texture2d::with_mipmaps(ctx, data, MipmapsOption::NoMipmap)?;
|
||||||
|
fonts.tex_id = TextureId::from(usize::MAX);
|
||||||
|
Ok(font_texture)
|
||||||
|
}
|
||||||
|
|
||||||
fn compile_default_program<F: Facade>(facade: &F) -> Result<Program, ProgramChooserCreationError> {
|
fn compile_default_program<F: Facade>(facade: &F) -> Result<Program, ProgramChooserCreationError> {
|
||||||
program!(
|
program!(
|
||||||
facade,
|
facade,
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
use parking_lot::ReentrantMutex;
|
use parking_lot::ReentrantMutex;
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::ops::Drop;
|
use std::ops::Drop;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::fonts::atlas::{FontAtlas, FontAtlasRefMut, FontId, SharedFontAtlas};
|
||||||
use crate::io::Io;
|
use crate::io::Io;
|
||||||
use crate::string::{ImStr, ImString};
|
use crate::string::{ImStr, ImString};
|
||||||
use crate::style::Style;
|
use crate::style::Style;
|
||||||
@ -46,6 +49,7 @@ use crate::Ui;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
raw: *mut sys::ImGuiContext,
|
raw: *mut sys::ImGuiContext,
|
||||||
|
shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>,
|
||||||
ini_filename: Option<ImString>,
|
ini_filename: Option<ImString>,
|
||||||
log_filename: Option<ImString>,
|
log_filename: Option<ImString>,
|
||||||
platform_name: Option<ImString>,
|
platform_name: Option<ImString>,
|
||||||
@ -75,7 +79,15 @@ impl Context {
|
|||||||
///
|
///
|
||||||
/// Panics if an active context already exists
|
/// Panics if an active context already exists
|
||||||
pub fn create() -> Self {
|
pub fn create() -> Self {
|
||||||
Self::create_internal()
|
Self::create_internal(None)
|
||||||
|
}
|
||||||
|
/// Creates a new active imgui-rs context with a shared font atlas.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if an active context already exists
|
||||||
|
pub fn create_with_shared_font_atlas(shared_font_atlas: Rc<RefCell<SharedFontAtlas>>) -> Self {
|
||||||
|
Self::create_internal(Some(shared_font_atlas))
|
||||||
}
|
}
|
||||||
/// Suspends this context so another context can be the active context.
|
/// Suspends this context so another context can be the active context.
|
||||||
pub fn suspend(self) -> SuspendedContext {
|
pub fn suspend(self) -> SuspendedContext {
|
||||||
@ -158,7 +170,7 @@ impl Context {
|
|||||||
let data = unsafe { CStr::from_ptr(sys::igSaveIniSettingsToMemory(ptr::null_mut())) };
|
let data = unsafe { CStr::from_ptr(sys::igSaveIniSettingsToMemory(ptr::null_mut())) };
|
||||||
buf.push_str(&data.to_string_lossy());
|
buf.push_str(&data.to_string_lossy());
|
||||||
}
|
}
|
||||||
fn create_internal() -> Self {
|
fn create_internal(shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>) -> Self {
|
||||||
let _guard = CTX_MUTEX.lock();
|
let _guard = CTX_MUTEX.lock();
|
||||||
assert!(
|
assert!(
|
||||||
no_current_context(),
|
no_current_context(),
|
||||||
@ -169,6 +181,7 @@ impl Context {
|
|||||||
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
||||||
Context {
|
Context {
|
||||||
raw,
|
raw,
|
||||||
|
shared_font_atlas,
|
||||||
ini_filename: None,
|
ini_filename: None,
|
||||||
log_filename: None,
|
log_filename: None,
|
||||||
platform_name: None,
|
platform_name: None,
|
||||||
@ -217,7 +230,11 @@ pub struct SuspendedContext(Context);
|
|||||||
impl SuspendedContext {
|
impl SuspendedContext {
|
||||||
/// Creates a new suspended imgui-rs context.
|
/// Creates a new suspended imgui-rs context.
|
||||||
pub fn create() -> Self {
|
pub fn create() -> Self {
|
||||||
Self::create_internal()
|
Self::create_internal(None)
|
||||||
|
}
|
||||||
|
/// Creates a new suspended imgui-rs context with a shared font atlas.
|
||||||
|
pub fn create_with_shared_font_atlas(shared_font_atlas: Rc<RefCell<SharedFontAtlas>>) -> Self {
|
||||||
|
Self::create_internal(Some(shared_font_atlas))
|
||||||
}
|
}
|
||||||
/// Attempts to activate this suspended context.
|
/// Attempts to activate this suspended context.
|
||||||
///
|
///
|
||||||
@ -236,11 +253,12 @@ impl SuspendedContext {
|
|||||||
Err(self)
|
Err(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn create_internal() -> Self {
|
fn create_internal(shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>) -> Self {
|
||||||
let _guard = CTX_MUTEX.lock();
|
let _guard = CTX_MUTEX.lock();
|
||||||
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
raw,
|
raw,
|
||||||
|
shared_font_atlas,
|
||||||
ini_filename: None,
|
ini_filename: None,
|
||||||
log_filename: None,
|
log_filename: None,
|
||||||
platform_name: None,
|
platform_name: None,
|
||||||
@ -323,6 +341,31 @@ fn test_suspend_failure() {
|
|||||||
assert!(suspended.activate().is_err());
|
assert!(suspended.activate().is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shared_font_atlas() {
|
||||||
|
let _guard = crate::test::TEST_MUTEX.lock();
|
||||||
|
let atlas = Rc::new(RefCell::new(SharedFontAtlas::create()));
|
||||||
|
let suspended1 = SuspendedContext::create_with_shared_font_atlas(atlas.clone());
|
||||||
|
let mut ctx2 = Context::create_with_shared_font_atlas(atlas.clone());
|
||||||
|
{
|
||||||
|
let _borrow = ctx2.fonts();
|
||||||
|
}
|
||||||
|
let _suspended2 = ctx2.suspend();
|
||||||
|
let mut ctx = suspended1.activate().unwrap();
|
||||||
|
let _borrow = ctx.fonts();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn test_shared_font_atlas_borrow_panic() {
|
||||||
|
let _guard = crate::test::TEST_MUTEX.lock();
|
||||||
|
let atlas = Rc::new(RefCell::new(SharedFontAtlas::create()));
|
||||||
|
let _suspended = SuspendedContext::create_with_shared_font_atlas(atlas.clone());
|
||||||
|
let mut ctx = Context::create_with_shared_font_atlas(atlas.clone());
|
||||||
|
let _borrow1 = atlas.borrow();
|
||||||
|
let _borrow2 = ctx.fonts();
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ini_load_save() {
|
fn test_ini_load_save() {
|
||||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||||
@ -365,10 +408,42 @@ impl Context {
|
|||||||
&mut *(sys::igGetStyle() as *mut Style)
|
&mut *(sys::igGetStyle() as *mut Style)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Returns a mutable reference to the font atlas.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the context uses a shared font atlas that is already borrowed
|
||||||
|
pub fn fonts(&mut self) -> FontAtlasRefMut {
|
||||||
|
match self.shared_font_atlas {
|
||||||
|
Some(ref font_atlas) => FontAtlasRefMut::Shared(font_atlas.borrow_mut()),
|
||||||
|
None => unsafe {
|
||||||
|
// safe because FontAtlas is a transparent wrapper around sys::ImFontAtlas
|
||||||
|
let fonts = &mut *(self.io_mut().fonts as *mut FontAtlas);
|
||||||
|
FontAtlasRefMut::Owned(fonts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the context uses a shared font atlas that is already borrowed
|
||||||
pub fn frame<'ui, 'a: 'ui>(&'a mut self) -> Ui<'ui> {
|
pub fn frame<'ui, 'a: 'ui>(&'a mut self) -> Ui<'ui> {
|
||||||
|
// Clear default font if it no longer exists. This could be an error in the future
|
||||||
|
let default_font = self.io().font_default;
|
||||||
|
if !default_font.is_null() && self.fonts().get_font(FontId(default_font)).is_none() {
|
||||||
|
self.io_mut().font_default = ptr::null_mut();
|
||||||
|
}
|
||||||
|
// NewFrame/Render/EndFrame mutate the font atlas so we need exclusive access to it
|
||||||
|
let font_atlas = self
|
||||||
|
.shared_font_atlas
|
||||||
|
.as_ref()
|
||||||
|
.map(|font_atlas| font_atlas.borrow_mut());
|
||||||
|
// TODO: precondition checks
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::igNewFrame();
|
sys::igNewFrame();
|
||||||
}
|
}
|
||||||
Ui { ctx: self }
|
Ui {
|
||||||
|
ctx: self,
|
||||||
|
font_atlas,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
399
src/fonts.rs
399
src/fonts.rs
@ -1,399 +0,0 @@
|
|||||||
use std::f32;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
use std::mem;
|
|
||||||
use std::os::raw::{c_float, c_int, c_void};
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use crate::internal::ImVector;
|
|
||||||
use crate::sys;
|
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
|
||||||
enum FontGlyphRangeData {
|
|
||||||
ChineseSimplifiedCommon,
|
|
||||||
ChineseFull,
|
|
||||||
Cyrillic,
|
|
||||||
Default,
|
|
||||||
Japanese,
|
|
||||||
Korean,
|
|
||||||
Thai,
|
|
||||||
Custom(*const sys::ImWchar),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A set of 16-bit Unicode codepoints
|
|
||||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
|
||||||
pub struct FontGlyphRange(FontGlyphRangeData);
|
|
||||||
impl FontGlyphRange {
|
|
||||||
/// The default set of glyph ranges used by imgui.
|
|
||||||
pub fn default() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::Default)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A set of glyph ranges appropriate for use with simplified common Chinese text.
|
|
||||||
pub fn chinese_simplified_common() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::ChineseSimplifiedCommon)
|
|
||||||
}
|
|
||||||
/// A set of glyph ranges appropriate for use with Chinese text.
|
|
||||||
pub fn chinese_full() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::ChineseFull)
|
|
||||||
}
|
|
||||||
/// A set of glyph ranges appropriate for use with Cyrillic text.
|
|
||||||
pub fn cyrillic() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::Cyrillic)
|
|
||||||
}
|
|
||||||
/// A set of glyph ranges appropriate for use with Japanese text.
|
|
||||||
pub fn japanese() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::Japanese)
|
|
||||||
}
|
|
||||||
/// A set of glyph ranges appropriate for use with Korean text.
|
|
||||||
pub fn korean() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::Korean)
|
|
||||||
}
|
|
||||||
/// A set of glyph ranges appropriate for use with Thai text.
|
|
||||||
pub fn thai() -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::Thai)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a glyph range from a static slice. The expected format is a series of pairs of
|
|
||||||
/// non-zero shorts, each representing an inclusive range of codepoints, followed by a single
|
|
||||||
/// zero terminating the range. The ranges must not overlap.
|
|
||||||
///
|
|
||||||
/// As the slice is expected to last as long as a font is used, and is written into global
|
|
||||||
/// state, it must be `'static`.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ======
|
|
||||||
///
|
|
||||||
/// This function will panic if the given slice is not a valid font range.
|
|
||||||
pub fn from_slice(slice: &'static [sys::ImWchar]) -> FontGlyphRange {
|
|
||||||
assert_eq!(
|
|
||||||
slice.len() % 2,
|
|
||||||
1,
|
|
||||||
"The length of a glyph range must be odd."
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
slice.last(),
|
|
||||||
Some(&0),
|
|
||||||
"A glyph range must be zero-terminated."
|
|
||||||
);
|
|
||||||
|
|
||||||
for (i, &glyph) in slice.iter().enumerate().take(slice.len() - 1) {
|
|
||||||
assert_ne!(
|
|
||||||
glyph, 0,
|
|
||||||
"A glyph in a range cannot be zero. \
|
|
||||||
(Glyph is zero at index {})",
|
|
||||||
i
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut ranges = Vec::new();
|
|
||||||
for i in 0..slice.len() / 2 {
|
|
||||||
let (start, end) = (slice[i * 2], slice[i * 2 + 1]);
|
|
||||||
assert!(
|
|
||||||
start <= end,
|
|
||||||
"The start of a range cannot be larger than its end. \
|
|
||||||
(At index {}, {} > {})",
|
|
||||||
i * 2,
|
|
||||||
start,
|
|
||||||
end
|
|
||||||
);
|
|
||||||
ranges.push((start, end));
|
|
||||||
}
|
|
||||||
ranges.sort_unstable_by_key(|x| x.0);
|
|
||||||
for i in 0..ranges.len() - 1 {
|
|
||||||
let (range_a, range_b) = (ranges[i], ranges[i + 1]);
|
|
||||||
if range_a.1 >= range_b.0 {
|
|
||||||
panic!(
|
|
||||||
"The glyph ranges {:?} and {:?} overlap between {:?}.",
|
|
||||||
range_a,
|
|
||||||
range_b,
|
|
||||||
(range_a.1, range_b.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { FontGlyphRange::from_slice_unchecked(slice) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a glyph range from a static slice without checking its validity.
|
|
||||||
///
|
|
||||||
/// See [`FontRangeGlyph::from_slice`] for more information.
|
|
||||||
pub unsafe fn from_slice_unchecked(slice: &'static [sys::ImWchar]) -> FontGlyphRange {
|
|
||||||
FontGlyphRange::from_ptr(slice.as_ptr())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a glyph range from a pointer, without checking its validity or enforcing its
|
|
||||||
/// lifetime. The memory the pointer points to must be valid for as long as the font is
|
|
||||||
/// in use.
|
|
||||||
pub unsafe fn from_ptr(ptr: *const sys::ImWchar) -> FontGlyphRange {
|
|
||||||
FontGlyphRange(FontGlyphRangeData::Custom(ptr))
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn to_ptr(&self, atlas: *mut sys::ImFontAtlas) -> *const sys::ImWchar {
|
|
||||||
match self.0 {
|
|
||||||
FontGlyphRangeData::ChineseFull => sys::ImFontAtlas_GetGlyphRangesChineseFull(atlas),
|
|
||||||
FontGlyphRangeData::ChineseSimplifiedCommon => {
|
|
||||||
sys::ImFontAtlas_GetGlyphRangesChineseSimplifiedCommon(atlas)
|
|
||||||
}
|
|
||||||
FontGlyphRangeData::Cyrillic => sys::ImFontAtlas_GetGlyphRangesCyrillic(atlas),
|
|
||||||
FontGlyphRangeData::Default => sys::ImFontAtlas_GetGlyphRangesDefault(atlas),
|
|
||||||
FontGlyphRangeData::Japanese => sys::ImFontAtlas_GetGlyphRangesJapanese(atlas),
|
|
||||||
FontGlyphRangeData::Korean => sys::ImFontAtlas_GetGlyphRangesKorean(atlas),
|
|
||||||
FontGlyphRangeData::Thai => sys::ImFontAtlas_GetGlyphRangesThai(atlas),
|
|
||||||
|
|
||||||
FontGlyphRangeData::Custom(ptr) => ptr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A builder for the configuration for a font.
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
|
||||||
pub struct ImFontConfig {
|
|
||||||
size_pixels: f32,
|
|
||||||
oversample_h: u32,
|
|
||||||
oversample_v: u32,
|
|
||||||
pixel_snap_h: bool,
|
|
||||||
glyph_extra_spacing: sys::ImVec2,
|
|
||||||
glyph_offset: sys::ImVec2,
|
|
||||||
merge_mode: bool,
|
|
||||||
rasterizer_multiply: f32,
|
|
||||||
}
|
|
||||||
impl ImFontConfig {
|
|
||||||
pub fn new() -> ImFontConfig {
|
|
||||||
ImFontConfig {
|
|
||||||
size_pixels: 0.0,
|
|
||||||
oversample_h: 3,
|
|
||||||
oversample_v: 1,
|
|
||||||
pixel_snap_h: false,
|
|
||||||
glyph_extra_spacing: sys::ImVec2::zero(),
|
|
||||||
glyph_offset: sys::ImVec2::zero(),
|
|
||||||
merge_mode: false,
|
|
||||||
rasterizer_multiply: 1.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn size_pixels(mut self, size_pixels: f32) -> ImFontConfig {
|
|
||||||
self.size_pixels = size_pixels;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn oversample_h(mut self, oversample_h: u32) -> ImFontConfig {
|
|
||||||
self.oversample_h = oversample_h;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn oversample_v(mut self, oversample_v: u32) -> ImFontConfig {
|
|
||||||
self.oversample_v = oversample_v;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn pixel_snap_h(mut self, pixel_snap_h: bool) -> ImFontConfig {
|
|
||||||
self.pixel_snap_h = pixel_snap_h;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn glyph_extra_spacing<I: Into<sys::ImVec2>>(mut self, extra_spacing: I) -> ImFontConfig {
|
|
||||||
self.glyph_extra_spacing = extra_spacing.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn glyph_offset<I: Into<sys::ImVec2>>(mut self, glyph_offset: I) -> ImFontConfig {
|
|
||||||
self.glyph_offset = glyph_offset.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn merge_mode(mut self, merge_mode: bool) -> ImFontConfig {
|
|
||||||
self.merge_mode = merge_mode;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn rasterizer_multiply(mut self, rasterizer_multiply: f32) -> ImFontConfig {
|
|
||||||
self.rasterizer_multiply = rasterizer_multiply;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn make_config(self) -> sys::ImFontConfig {
|
|
||||||
let mut config = unsafe {
|
|
||||||
let mut config = mem::zeroed::<sys::ImFontConfig>();
|
|
||||||
config.FontDataOwnedByAtlas = true;
|
|
||||||
config.GlyphMaxAdvanceX = f32::MAX as c_float;
|
|
||||||
config
|
|
||||||
};
|
|
||||||
config.SizePixels = self.size_pixels;
|
|
||||||
config.OversampleH = self.oversample_h as c_int;
|
|
||||||
config.OversampleV = self.oversample_v as c_int;
|
|
||||||
config.PixelSnapH = self.pixel_snap_h;
|
|
||||||
config.GlyphExtraSpacing = self.glyph_extra_spacing;
|
|
||||||
config.GlyphOffset = self.glyph_offset;
|
|
||||||
config.MergeMode = self.merge_mode;
|
|
||||||
config.RasterizerMultiply = self.rasterizer_multiply;
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a custom font to the font set with the given configuration. A font size must be set
|
|
||||||
/// in the configuration.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ======
|
|
||||||
///
|
|
||||||
/// If no font size is set for the configuration.
|
|
||||||
pub fn add_font<'a>(
|
|
||||||
self,
|
|
||||||
atlas: &'a mut ImFontAtlas<'a>,
|
|
||||||
data: &[u8],
|
|
||||||
range: &FontGlyphRange,
|
|
||||||
) -> ImFont<'a> {
|
|
||||||
atlas.add_font_with_config(data, self, range)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the default font to a given atlas using this configuration.
|
|
||||||
pub fn add_default_font<'a>(self, atlas: &'a mut ImFontAtlas<'a>) -> ImFont<'a> {
|
|
||||||
atlas.add_default_font_with_config(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Default for ImFontConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
ImFontConfig::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handle to an imgui font.
|
|
||||||
pub struct ImFont<'a> {
|
|
||||||
font: *mut sys::ImFont,
|
|
||||||
_phantom: PhantomData<&'a mut sys::ImFont>,
|
|
||||||
}
|
|
||||||
impl<'a> ImFont<'a> {
|
|
||||||
unsafe fn from_ptr(font: *mut sys::ImFont) -> ImFont<'a> {
|
|
||||||
ImFont {
|
|
||||||
font,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn chain(&mut self) -> ImFont {
|
|
||||||
ImFont {
|
|
||||||
font: self.font,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn font_size(&self) -> f32 {
|
|
||||||
unsafe { (*self.font).FontSize }
|
|
||||||
}
|
|
||||||
pub fn set_font_size(&mut self, size: f32) -> ImFont {
|
|
||||||
unsafe {
|
|
||||||
(*self.font).FontSize = size;
|
|
||||||
}
|
|
||||||
self.chain()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn scale(&self) -> f32 {
|
|
||||||
unsafe { (*self.font).Scale }
|
|
||||||
}
|
|
||||||
pub fn set_scale(&mut self, size: f32) -> ImFont {
|
|
||||||
unsafe {
|
|
||||||
(*self.font).Scale = size;
|
|
||||||
}
|
|
||||||
self.chain()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn display_offset(&self) -> (f32, f32) {
|
|
||||||
unsafe { (*self.font).DisplayOffset.into() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A handle to imgui's font manager.
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ImFontAtlas<'a> {
|
|
||||||
atlas: *mut sys::ImFontAtlas,
|
|
||||||
_phantom: PhantomData<&'a mut sys::ImFontAtlas>,
|
|
||||||
}
|
|
||||||
impl<'a> ImFontAtlas<'a> {
|
|
||||||
pub(crate) unsafe fn from_ptr(atlas: *mut sys::ImFontAtlas) -> ImFontAtlas<'a> {
|
|
||||||
ImFontAtlas {
|
|
||||||
atlas,
|
|
||||||
_phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the default font to the font set.
|
|
||||||
pub fn add_default_font(&mut self) -> ImFont {
|
|
||||||
unsafe { ImFont::from_ptr(sys::ImFontAtlas_AddFontDefault(self.atlas, ptr::null_mut())) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds the default fnt to the font set with the given configuration.
|
|
||||||
pub fn add_default_font_with_config(&mut self, config: ImFontConfig) -> ImFont {
|
|
||||||
let config = config.make_config();
|
|
||||||
unsafe { ImFont::from_ptr(sys::ImFontAtlas_AddFontDefault(self.atlas, &config)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn raw_add_font(
|
|
||||||
&mut self,
|
|
||||||
data: &[u8],
|
|
||||||
config: ImFontConfig,
|
|
||||||
range: &FontGlyphRange,
|
|
||||||
) -> ImFont {
|
|
||||||
assert!(
|
|
||||||
(data.len() as u64) < (c_int::max_value() as u64),
|
|
||||||
"Font data is too long."
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
let mut config = config.make_config();
|
|
||||||
assert!(config.SizePixels > 0.0, "Font size cannot be zero.");
|
|
||||||
config.FontData = data.as_ptr() as *mut c_void;
|
|
||||||
config.FontDataSize = data.len() as c_int;
|
|
||||||
config.GlyphRanges = range.to_ptr(self.atlas);
|
|
||||||
config.FontDataOwnedByAtlas = false;
|
|
||||||
|
|
||||||
ImFont::from_ptr(sys::ImFontAtlas_AddFont(self.atlas, &config))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a custom font to the font set.
|
|
||||||
pub fn add_font(&mut self, data: &[u8], size: f32, range: &FontGlyphRange) -> ImFont {
|
|
||||||
self.raw_add_font(data, ImFontConfig::new().size_pixels(size), range)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a custom font to the font set with the given configuration. A font size must be set
|
|
||||||
/// in the configuration.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ======
|
|
||||||
///
|
|
||||||
/// If no font size is set for the configuration.
|
|
||||||
pub fn add_font_with_config(
|
|
||||||
&mut self,
|
|
||||||
data: &[u8],
|
|
||||||
config: ImFontConfig,
|
|
||||||
range: &FontGlyphRange,
|
|
||||||
) -> ImFont {
|
|
||||||
self.raw_add_font(data, config, range)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The number of fonts currently registered in the atlas.
|
|
||||||
pub fn font_count(&self) -> usize {
|
|
||||||
unsafe { (*self.atlas).Fonts.Size as usize }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a font from the atlas.
|
|
||||||
///
|
|
||||||
/// Panics
|
|
||||||
/// ======
|
|
||||||
///
|
|
||||||
/// Panics if the index is out of range.
|
|
||||||
pub fn index_font(&mut self, index: usize) -> ImFont {
|
|
||||||
let fonts = unsafe {
|
|
||||||
let fonts: &sys::ImVector_ImFontPtr = &(*self.atlas).Fonts;
|
|
||||||
let fonts: &ImVector<*mut sys::ImFont> = ::std::mem::transmute(fonts);
|
|
||||||
fonts.as_slice()
|
|
||||||
};
|
|
||||||
assert!(index < fonts.len(), "Font index is out of range.");
|
|
||||||
unsafe { ImFont::from_ptr(fonts[index]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Clears all fonts associated with this texture atlas.
|
|
||||||
pub fn clear(&mut self) {
|
|
||||||
unsafe { sys::ImFontAtlas_Clear(self.atlas) }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn texture_id(&self) -> usize {
|
|
||||||
unsafe { (*self.atlas).TexID as usize }
|
|
||||||
}
|
|
||||||
pub fn set_texture_id(&mut self, value: usize) {
|
|
||||||
unsafe {
|
|
||||||
(*self.atlas).TexID = value as *mut c_void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
442
src/fonts/atlas.rs
Normal file
442
src/fonts/atlas.rs
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
use bitflags::bitflags;
|
||||||
|
use std::cell;
|
||||||
|
use std::f32;
|
||||||
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::os::raw::{c_int, c_uchar, c_void};
|
||||||
|
use std::ptr;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
use crate::fonts::font::Font;
|
||||||
|
use crate::fonts::glyph_ranges::FontGlyphRanges;
|
||||||
|
use crate::internal::{ImVector, RawCast};
|
||||||
|
use crate::sys;
|
||||||
|
use crate::TextureId;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Font atlas configuration flags
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct FontAtlasFlags: u32 {
|
||||||
|
const NO_POWER_OF_TWO_HEIGHT = sys::ImFontAtlasFlags_NoPowerOfTwoHeight;
|
||||||
|
const NO_MOUSE_CURSORS = sys::ImFontAtlasFlags_NoMouseCursors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct FontId(pub(crate) *const Font);
|
||||||
|
|
||||||
|
/// A font atlas that builds a single texture
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FontAtlas {
|
||||||
|
locked: bool,
|
||||||
|
/// Configuration flags
|
||||||
|
pub flags: FontAtlasFlags,
|
||||||
|
/// Texture identifier
|
||||||
|
pub tex_id: TextureId,
|
||||||
|
/// Texture width desired by user before building the atlas.
|
||||||
|
///
|
||||||
|
/// Must be a power-of-two. If you have many glyphs and your graphics API has texture size
|
||||||
|
/// restrictions, you may want to increase texture width to decrease the height.
|
||||||
|
pub tex_desired_width: i32,
|
||||||
|
/// Padding between glyphs within texture in pixels.
|
||||||
|
///
|
||||||
|
/// Defaults to 1. If your rendering method doesn't rely on bilinear filtering, you may set
|
||||||
|
/// this to 0.
|
||||||
|
pub tex_glyph_padding: i32,
|
||||||
|
|
||||||
|
tex_pixels_alpha8: *mut u8,
|
||||||
|
tex_pixels_rgba32: *mut u32,
|
||||||
|
tex_width: i32,
|
||||||
|
tex_height: i32,
|
||||||
|
tex_uv_scale: [f32; 2],
|
||||||
|
tex_uv_white_pixel: [f32; 2],
|
||||||
|
fonts: ImVector<*mut Font>,
|
||||||
|
custom_rects: sys::ImVector_CustomRect,
|
||||||
|
config_data: sys::ImVector_ImFontConfig,
|
||||||
|
custom_rect_ids: [i32; 1],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl RawCast<sys::ImFontAtlas> for FontAtlas {}
|
||||||
|
|
||||||
|
impl FontAtlas {
|
||||||
|
pub fn add_font(&mut self, font_sources: &[FontSource]) -> FontId {
|
||||||
|
let (head, tail) = font_sources.split_first().unwrap();
|
||||||
|
let font_id = self.add_font_internal(head, false);
|
||||||
|
for font in tail {
|
||||||
|
self.add_font_internal(font, true);
|
||||||
|
}
|
||||||
|
font_id
|
||||||
|
}
|
||||||
|
fn add_font_internal(&mut self, font_source: &FontSource, merge_mode: bool) -> FontId {
|
||||||
|
let mut raw_config = sys_font_config_default();
|
||||||
|
raw_config.MergeMode = merge_mode;
|
||||||
|
let raw_font = match font_source {
|
||||||
|
FontSource::DefaultFontData { config } => unsafe {
|
||||||
|
if let Some(config) = config {
|
||||||
|
config.apply_to_raw_config(&mut raw_config, self.raw_mut());
|
||||||
|
}
|
||||||
|
sys::ImFontAtlas_AddFontDefault(self.raw_mut(), &raw_config)
|
||||||
|
},
|
||||||
|
FontSource::TtfData {
|
||||||
|
data,
|
||||||
|
size_pixels,
|
||||||
|
config,
|
||||||
|
} => {
|
||||||
|
if let Some(config) = config {
|
||||||
|
unsafe {
|
||||||
|
config.apply_to_raw_config(&mut raw_config, self.raw_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We can't guarantee `data` is alive when the font atlas is built, so
|
||||||
|
// make a copy and move ownership of the data to the atlas
|
||||||
|
let data_copy = unsafe {
|
||||||
|
let ptr = sys::igMemAlloc(data.len()) as *mut u8;
|
||||||
|
assert!(!ptr.is_null());
|
||||||
|
slice::from_raw_parts_mut(ptr, data.len())
|
||||||
|
};
|
||||||
|
data_copy.copy_from_slice(data);
|
||||||
|
raw_config.FontData = data_copy.as_mut_ptr() as *mut c_void;
|
||||||
|
raw_config.FontDataSize = data_copy.len() as i32;
|
||||||
|
raw_config.FontDataOwnedByAtlas = true;
|
||||||
|
raw_config.SizePixels = *size_pixels;
|
||||||
|
unsafe { sys::ImFontAtlas_AddFont(self.raw_mut(), &raw_config) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FontId(raw_font as *const _)
|
||||||
|
}
|
||||||
|
pub fn fonts(&self) -> Vec<FontId> {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
unsafe {
|
||||||
|
for &font in self.fonts.as_slice() {
|
||||||
|
result.push((*font).id());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
pub fn get_font(&self, id: FontId) -> Option<&Font> {
|
||||||
|
unsafe {
|
||||||
|
for &font in self.fonts.as_slice() {
|
||||||
|
if id == FontId(font) {
|
||||||
|
return Some(&*(font as *const Font));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
/// Returns true if the font atlas has been built
|
||||||
|
pub fn is_built(&self) -> bool {
|
||||||
|
unsafe { sys::ImFontAtlas_IsBuilt(self.raw() as *const sys::ImFontAtlas as *mut _) }
|
||||||
|
}
|
||||||
|
/// Builds a 1 byte per-pixel font atlas texture
|
||||||
|
pub fn build_alpha8_texture(&mut self) -> FontAtlasTexture {
|
||||||
|
let mut pixels: *mut c_uchar = ptr::null_mut();
|
||||||
|
let mut width: c_int = 0;
|
||||||
|
let mut height: c_int = 0;
|
||||||
|
let mut bytes_per_pixel: c_int = 0;
|
||||||
|
unsafe {
|
||||||
|
sys::ImFontAtlas_GetTexDataAsAlpha8(
|
||||||
|
self.raw_mut(),
|
||||||
|
&mut pixels,
|
||||||
|
&mut width,
|
||||||
|
&mut height,
|
||||||
|
&mut bytes_per_pixel,
|
||||||
|
);
|
||||||
|
assert!(width >= 0, "font texture width must be positive");
|
||||||
|
assert!(height >= 0, "font texture height must be positive");
|
||||||
|
assert!(
|
||||||
|
bytes_per_pixel >= 0,
|
||||||
|
"font texture bytes per pixel must be positive"
|
||||||
|
);
|
||||||
|
let height = height as usize;
|
||||||
|
// Check multiplication to avoid constructing an invalid slice in case of overflow
|
||||||
|
let pitch = width
|
||||||
|
.checked_mul(bytes_per_pixel)
|
||||||
|
.expect("Overflow in font texture pitch calculation")
|
||||||
|
as usize;
|
||||||
|
FontAtlasTexture {
|
||||||
|
width: width as u32,
|
||||||
|
height: height as u32,
|
||||||
|
data: slice::from_raw_parts(pixels, pitch * height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Builds a 4 byte per-pixel font atlas texture
|
||||||
|
pub fn build_rgba32_texture(&mut self) -> FontAtlasTexture {
|
||||||
|
let mut pixels: *mut c_uchar = ptr::null_mut();
|
||||||
|
let mut width: c_int = 0;
|
||||||
|
let mut height: c_int = 0;
|
||||||
|
let mut bytes_per_pixel: c_int = 0;
|
||||||
|
unsafe {
|
||||||
|
sys::ImFontAtlas_GetTexDataAsRGBA32(
|
||||||
|
self.raw_mut(),
|
||||||
|
&mut pixels,
|
||||||
|
&mut width,
|
||||||
|
&mut height,
|
||||||
|
&mut bytes_per_pixel,
|
||||||
|
);
|
||||||
|
assert!(width >= 0, "font texture width must be positive");
|
||||||
|
assert!(height >= 0, "font texture height must be positive");
|
||||||
|
assert!(
|
||||||
|
bytes_per_pixel >= 0,
|
||||||
|
"font texture bytes per pixel must be positive"
|
||||||
|
);
|
||||||
|
let height = height as usize;
|
||||||
|
// Check multiplication to avoid constructing an invalid slice in case of overflow
|
||||||
|
let pitch = width
|
||||||
|
.checked_mul(bytes_per_pixel)
|
||||||
|
.expect("Overflow in font texture pitch calculation")
|
||||||
|
as usize;
|
||||||
|
FontAtlasTexture {
|
||||||
|
width: width as u32,
|
||||||
|
height: height as u32,
|
||||||
|
data: slice::from_raw_parts(pixels, pitch * height),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Clears the font atlas completely (both input and output data)
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::ImFontAtlas_Clear(self.raw_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Clears output font data (glyph storage, UV coordinates)
|
||||||
|
pub fn clear_fonts(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::ImFontAtlas_ClearFonts(self.raw_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Clears output texture data.
|
||||||
|
///
|
||||||
|
/// Can be used to save RAM once the texture has been transferred to the GPU.
|
||||||
|
pub fn clear_tex_data(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::ImFontAtlas_ClearTexData(self.raw_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Clears all the data used to build the textures and fonts
|
||||||
|
pub fn clear_input_data(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
sys::ImFontAtlas_ClearInputData(self.raw_mut());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_font_atlas_memory_layout() {
|
||||||
|
use std::mem;
|
||||||
|
assert_eq!(
|
||||||
|
mem::size_of::<FontAtlas>(),
|
||||||
|
mem::size_of::<sys::ImFontAtlas>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
mem::align_of::<FontAtlas>(),
|
||||||
|
mem::align_of::<sys::ImFontAtlas>()
|
||||||
|
);
|
||||||
|
use memoffset::offset_of;
|
||||||
|
macro_rules! assert_field_offset {
|
||||||
|
($l:ident, $r:ident) => {
|
||||||
|
assert_eq!(offset_of!(FontAtlas, $l), offset_of!(sys::ImFontAtlas, $r));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
assert_field_offset!(locked, Locked);
|
||||||
|
assert_field_offset!(flags, Flags);
|
||||||
|
assert_field_offset!(tex_id, TexID);
|
||||||
|
assert_field_offset!(tex_desired_width, TexDesiredWidth);
|
||||||
|
assert_field_offset!(tex_glyph_padding, TexGlyphPadding);
|
||||||
|
assert_field_offset!(tex_pixels_alpha8, TexPixelsAlpha8);
|
||||||
|
assert_field_offset!(tex_pixels_rgba32, TexPixelsRGBA32);
|
||||||
|
assert_field_offset!(tex_width, TexWidth);
|
||||||
|
assert_field_offset!(tex_height, TexHeight);
|
||||||
|
assert_field_offset!(tex_uv_scale, TexUvScale);
|
||||||
|
assert_field_offset!(tex_uv_white_pixel, TexUvWhitePixel);
|
||||||
|
assert_field_offset!(fonts, Fonts);
|
||||||
|
assert_field_offset!(custom_rects, CustomRects);
|
||||||
|
assert_field_offset!(config_data, ConfigData);
|
||||||
|
assert_field_offset!(custom_rect_ids, CustomRectIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum FontSource<'a> {
|
||||||
|
DefaultFontData {
|
||||||
|
config: Option<FontConfig>,
|
||||||
|
},
|
||||||
|
TtfData {
|
||||||
|
data: &'a [u8],
|
||||||
|
size_pixels: f32,
|
||||||
|
config: Option<FontConfig>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FontConfig {
|
||||||
|
pub size_pixels: f32,
|
||||||
|
pub oversample_h: i32,
|
||||||
|
pub oversample_v: i32,
|
||||||
|
pub pixel_snap_h: bool,
|
||||||
|
pub glyph_extra_spacing: [f32; 2],
|
||||||
|
pub glyph_offset: [f32; 2],
|
||||||
|
pub glyph_ranges: FontGlyphRanges,
|
||||||
|
pub glyph_min_advance_x: f32,
|
||||||
|
pub glyph_max_advance_x: f32,
|
||||||
|
pub rasterizer_flags: u32,
|
||||||
|
pub rasterizer_multiply: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FontConfig {
|
||||||
|
fn default() -> FontConfig {
|
||||||
|
FontConfig {
|
||||||
|
size_pixels: 0.0,
|
||||||
|
oversample_h: 3,
|
||||||
|
oversample_v: 1,
|
||||||
|
pixel_snap_h: false,
|
||||||
|
glyph_extra_spacing: [0.0, 0.0],
|
||||||
|
glyph_offset: [0.0, 0.0],
|
||||||
|
glyph_ranges: FontGlyphRanges::default(),
|
||||||
|
glyph_min_advance_x: 0.0,
|
||||||
|
glyph_max_advance_x: f32::MAX,
|
||||||
|
rasterizer_flags: 0,
|
||||||
|
rasterizer_multiply: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontConfig {
|
||||||
|
fn apply_to_raw_config(&self, raw: &mut sys::ImFontConfig, atlas: *mut sys::ImFontAtlas) {
|
||||||
|
raw.SizePixels = self.size_pixels;
|
||||||
|
raw.OversampleH = self.oversample_h;
|
||||||
|
raw.OversampleV = self.oversample_v;
|
||||||
|
raw.PixelSnapH = self.pixel_snap_h;
|
||||||
|
raw.GlyphExtraSpacing = self.glyph_extra_spacing.into();
|
||||||
|
raw.GlyphOffset = self.glyph_offset.into();
|
||||||
|
raw.GlyphRanges = unsafe { self.glyph_ranges.to_ptr(atlas) };
|
||||||
|
raw.GlyphMinAdvanceX = self.glyph_min_advance_x;
|
||||||
|
raw.GlyphMaxAdvanceX = self.glyph_max_advance_x;
|
||||||
|
raw.RasterizerFlags = self.rasterizer_flags;
|
||||||
|
raw.RasterizerMultiply = self.rasterizer_multiply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sys_font_config_default() -> sys::ImFontConfig {
|
||||||
|
unsafe {
|
||||||
|
let heap_allocated = sys::ImFontConfig_ImFontConfig();
|
||||||
|
let copy = *heap_allocated;
|
||||||
|
sys::ImFontConfig_destroy(heap_allocated);
|
||||||
|
copy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_font_config_default() {
|
||||||
|
let sys_font_config = sys_font_config_default();
|
||||||
|
let font_config = FontConfig::default();
|
||||||
|
assert_eq!(font_config.size_pixels, sys_font_config.SizePixels);
|
||||||
|
assert_eq!(font_config.oversample_h, sys_font_config.OversampleH);
|
||||||
|
assert_eq!(font_config.oversample_v, sys_font_config.OversampleV);
|
||||||
|
assert_eq!(font_config.pixel_snap_h, sys_font_config.PixelSnapH);
|
||||||
|
assert_eq!(
|
||||||
|
font_config.glyph_extra_spacing[0],
|
||||||
|
sys_font_config.GlyphExtraSpacing.x
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
font_config.glyph_extra_spacing[1],
|
||||||
|
sys_font_config.GlyphExtraSpacing.y
|
||||||
|
);
|
||||||
|
assert_eq!(font_config.glyph_offset[0], sys_font_config.GlyphOffset.x);
|
||||||
|
assert_eq!(font_config.glyph_offset[1], sys_font_config.GlyphOffset.y);
|
||||||
|
assert_eq!(
|
||||||
|
font_config.glyph_min_advance_x,
|
||||||
|
sys_font_config.GlyphMinAdvanceX
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
font_config.glyph_max_advance_x,
|
||||||
|
sys_font_config.GlyphMaxAdvanceX
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
font_config.rasterizer_flags,
|
||||||
|
sys_font_config.RasterizerFlags
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
font_config.rasterizer_multiply,
|
||||||
|
sys_font_config.RasterizerMultiply
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handle to a font atlas texture
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FontAtlasTexture<'a> {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub data: &'a [u8],
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A font atlas that can be shared between contexts
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SharedFontAtlas(*mut sys::ImFontAtlas);
|
||||||
|
|
||||||
|
impl SharedFontAtlas {
|
||||||
|
pub fn create() -> SharedFontAtlas {
|
||||||
|
SharedFontAtlas(unsafe { sys::ImFontAtlas_ImFontAtlas() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for SharedFontAtlas {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { sys::ImFontAtlas_destroy(self.0) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for SharedFontAtlas {
|
||||||
|
type Target = FontAtlas;
|
||||||
|
fn deref(&self) -> &FontAtlas {
|
||||||
|
unsafe { &*(self.0 as *const FontAtlas) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for SharedFontAtlas {
|
||||||
|
fn deref_mut(&mut self) -> &mut FontAtlas {
|
||||||
|
unsafe { &mut *(self.0 as *mut FontAtlas) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An immutably borrowed reference to a (possibly shared) font atlas
|
||||||
|
pub enum FontAtlasRef<'a> {
|
||||||
|
Owned(&'a FontAtlas),
|
||||||
|
Shared(&'a cell::RefMut<'a, SharedFontAtlas>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for FontAtlasRef<'a> {
|
||||||
|
type Target = FontAtlas;
|
||||||
|
fn deref(&self) -> &FontAtlas {
|
||||||
|
use self::FontAtlasRef::*;
|
||||||
|
match self {
|
||||||
|
Owned(atlas) => atlas,
|
||||||
|
Shared(cell) => cell,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A mutably borrowed reference to a (possibly shared) font atlas
|
||||||
|
pub enum FontAtlasRefMut<'a> {
|
||||||
|
Owned(&'a mut FontAtlas),
|
||||||
|
Shared(cell::RefMut<'a, SharedFontAtlas>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Deref for FontAtlasRefMut<'a> {
|
||||||
|
type Target = FontAtlas;
|
||||||
|
fn deref(&self) -> &FontAtlas {
|
||||||
|
use self::FontAtlasRefMut::*;
|
||||||
|
match self {
|
||||||
|
Owned(atlas) => atlas,
|
||||||
|
Shared(cell) => cell,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerefMut for FontAtlasRefMut<'a> {
|
||||||
|
fn deref_mut(&mut self) -> &mut FontAtlas {
|
||||||
|
use self::FontAtlasRefMut::*;
|
||||||
|
match self {
|
||||||
|
Owned(atlas) => atlas,
|
||||||
|
Shared(cell) => cell,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/fonts/font.rs
Normal file
63
src/fonts/font.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
|
use crate::fonts::atlas::{FontAtlas, FontId};
|
||||||
|
use crate::fonts::glyph::FontGlyph;
|
||||||
|
use crate::internal::{ImVector, RawCast};
|
||||||
|
use crate::sys;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Font {
|
||||||
|
index_advance_x: ImVector<f32>,
|
||||||
|
pub fallback_advance_x: f32,
|
||||||
|
pub font_size: f32,
|
||||||
|
index_lookup: ImVector<sys::ImWchar>,
|
||||||
|
glyphs: ImVector<FontGlyph>,
|
||||||
|
fallback_glyph: *const FontGlyph,
|
||||||
|
pub display_offset: [f32; 2],
|
||||||
|
container_atlas: *mut FontAtlas,
|
||||||
|
config_data: *const sys::ImFontConfig,
|
||||||
|
pub config_data_count: i16,
|
||||||
|
pub fallback_char: sys::ImWchar,
|
||||||
|
pub scale: f32,
|
||||||
|
pub ascent: f32,
|
||||||
|
pub descent: f32,
|
||||||
|
pub metrics_total_surface: c_int,
|
||||||
|
pub dirty_lookup_tables: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl RawCast<sys::ImFont> for Font {}
|
||||||
|
|
||||||
|
impl Font {
|
||||||
|
pub fn id(&self) -> FontId {
|
||||||
|
FontId(self as *const _)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_font_memory_layout() {
|
||||||
|
use std::mem;
|
||||||
|
assert_eq!(mem::size_of::<Font>(), mem::size_of::<sys::ImFont>());
|
||||||
|
assert_eq!(mem::align_of::<Font>(), mem::align_of::<sys::ImFont>());
|
||||||
|
use memoffset::offset_of;
|
||||||
|
macro_rules! assert_field_offset {
|
||||||
|
($l:ident, $r:ident) => {
|
||||||
|
assert_eq!(offset_of!(Font, $l), offset_of!(sys::ImFont, $r));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
assert_field_offset!(index_advance_x, IndexAdvanceX);
|
||||||
|
assert_field_offset!(fallback_advance_x, FallbackAdvanceX);
|
||||||
|
assert_field_offset!(font_size, FontSize);
|
||||||
|
assert_field_offset!(index_lookup, IndexLookup);
|
||||||
|
assert_field_offset!(glyphs, Glyphs);
|
||||||
|
assert_field_offset!(fallback_glyph, FallbackGlyph);
|
||||||
|
assert_field_offset!(display_offset, DisplayOffset);
|
||||||
|
assert_field_offset!(container_atlas, ContainerAtlas);
|
||||||
|
assert_field_offset!(config_data, ConfigData);
|
||||||
|
assert_field_offset!(config_data_count, ConfigDataCount);
|
||||||
|
assert_field_offset!(fallback_char, FallbackChar);
|
||||||
|
assert_field_offset!(scale, Scale);
|
||||||
|
assert_field_offset!(ascent, Ascent);
|
||||||
|
assert_field_offset!(descent, Descent);
|
||||||
|
assert_field_offset!(metrics_total_surface, MetricsTotalSurface);
|
||||||
|
assert_field_offset!(dirty_lookup_tables, DirtyLookupTables);
|
||||||
|
}
|
||||||
48
src/fonts/glyph.rs
Normal file
48
src/fonts/glyph.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use crate::internal::RawCast;
|
||||||
|
use crate::sys;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FontGlyph {
|
||||||
|
pub codepoint: u16,
|
||||||
|
pub advance_x: f32,
|
||||||
|
pub x0: f32,
|
||||||
|
pub y0: f32,
|
||||||
|
pub x1: f32,
|
||||||
|
pub y1: f32,
|
||||||
|
pub u0: f32,
|
||||||
|
pub v0: f32,
|
||||||
|
pub u1: f32,
|
||||||
|
pub v1: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl RawCast<sys::ImFontGlyph> for FontGlyph {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_font_glyph_memory_layout() {
|
||||||
|
use std::mem;
|
||||||
|
assert_eq!(
|
||||||
|
mem::size_of::<FontGlyph>(),
|
||||||
|
mem::size_of::<sys::ImFontGlyph>()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
mem::align_of::<FontGlyph>(),
|
||||||
|
mem::align_of::<sys::ImFontGlyph>()
|
||||||
|
);
|
||||||
|
use memoffset::offset_of;
|
||||||
|
macro_rules! assert_field_offset {
|
||||||
|
($l:ident, $r:ident) => {
|
||||||
|
assert_eq!(offset_of!(FontGlyph, $l), offset_of!(sys::ImFontGlyph, $r));
|
||||||
|
};
|
||||||
|
};
|
||||||
|
assert_field_offset!(codepoint, Codepoint);
|
||||||
|
assert_field_offset!(advance_x, AdvanceX);
|
||||||
|
assert_field_offset!(x0, X0);
|
||||||
|
assert_field_offset!(y0, Y0);
|
||||||
|
assert_field_offset!(x1, X1);
|
||||||
|
assert_field_offset!(y1, Y1);
|
||||||
|
assert_field_offset!(u0, U0);
|
||||||
|
assert_field_offset!(v0, V0);
|
||||||
|
assert_field_offset!(u1, U1);
|
||||||
|
assert_field_offset!(v1, V1);
|
||||||
|
}
|
||||||
143
src/fonts/glyph_ranges.rs
Normal file
143
src/fonts/glyph_ranges.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use crate::sys;
|
||||||
|
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
enum FontGlyphRangeData {
|
||||||
|
ChineseSimplifiedCommon,
|
||||||
|
ChineseFull,
|
||||||
|
Cyrillic,
|
||||||
|
Default,
|
||||||
|
Japanese,
|
||||||
|
Korean,
|
||||||
|
Thai,
|
||||||
|
Vietnamese,
|
||||||
|
Custom(*const sys::ImWchar),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A set of 16-bit Unicode codepoints
|
||||||
|
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||||
|
pub struct FontGlyphRanges(FontGlyphRangeData);
|
||||||
|
impl FontGlyphRanges {
|
||||||
|
/// The default set of glyph ranges used by imgui.
|
||||||
|
pub fn default() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Default)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with simplified common Chinese text.
|
||||||
|
pub fn chinese_simplified_common() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::ChineseSimplifiedCommon)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with Chinese text.
|
||||||
|
pub fn chinese_full() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::ChineseFull)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with Cyrillic text.
|
||||||
|
pub fn cyrillic() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Cyrillic)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with Japanese text.
|
||||||
|
pub fn japanese() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Japanese)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with Korean text.
|
||||||
|
pub fn korean() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Korean)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with Thai text.
|
||||||
|
pub fn thai() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Thai)
|
||||||
|
}
|
||||||
|
/// A set of glyph ranges appropriate for use with Vietnamese text.
|
||||||
|
pub fn vietnamese() -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Vietnamese)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a glyph range from a static slice. The expected format is a series of pairs of
|
||||||
|
/// non-zero shorts, each representing an inclusive range of codepoints, followed by a single
|
||||||
|
/// zero terminating the range. The ranges must not overlap.
|
||||||
|
///
|
||||||
|
/// As the slice is expected to last as long as a font is used, and is written into global
|
||||||
|
/// state, it must be `'static`.
|
||||||
|
///
|
||||||
|
/// Panics
|
||||||
|
/// ======
|
||||||
|
///
|
||||||
|
/// This function will panic if the given slice is not a valid font range.
|
||||||
|
pub fn from_slice(slice: &'static [u16]) -> FontGlyphRanges {
|
||||||
|
assert_eq!(
|
||||||
|
slice.len() % 2,
|
||||||
|
1,
|
||||||
|
"The length of a glyph range must be odd."
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
slice.last(),
|
||||||
|
Some(&0),
|
||||||
|
"A glyph range must be zero-terminated."
|
||||||
|
);
|
||||||
|
|
||||||
|
for (i, &glyph) in slice.iter().enumerate().take(slice.len() - 1) {
|
||||||
|
assert_ne!(
|
||||||
|
glyph, 0,
|
||||||
|
"A glyph in a range cannot be zero. \
|
||||||
|
(Glyph is zero at index {})",
|
||||||
|
i
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ranges = Vec::new();
|
||||||
|
for i in 0..slice.len() / 2 {
|
||||||
|
let (start, end) = (slice[i * 2], slice[i * 2 + 1]);
|
||||||
|
assert!(
|
||||||
|
start <= end,
|
||||||
|
"The start of a range cannot be larger than its end. \
|
||||||
|
(At index {}, {} > {})",
|
||||||
|
i * 2,
|
||||||
|
start,
|
||||||
|
end
|
||||||
|
);
|
||||||
|
ranges.push((start, end));
|
||||||
|
}
|
||||||
|
ranges.sort_unstable_by_key(|x| x.0);
|
||||||
|
for i in 0..ranges.len() - 1 {
|
||||||
|
let (range_a, range_b) = (ranges[i], ranges[i + 1]);
|
||||||
|
if range_a.1 >= range_b.0 {
|
||||||
|
panic!(
|
||||||
|
"The glyph ranges {:?} and {:?} overlap between {:?}.",
|
||||||
|
range_a,
|
||||||
|
range_b,
|
||||||
|
(range_a.1, range_b.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { FontGlyphRanges::from_slice_unchecked(slice) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a glyph range from a static slice without checking its validity.
|
||||||
|
///
|
||||||
|
/// See [`FontRangeGlyph::from_slice`] for more information.
|
||||||
|
pub unsafe fn from_slice_unchecked(slice: &'static [u16]) -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges::from_ptr(slice.as_ptr())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a glyph range from a pointer, without checking its validity or enforcing its
|
||||||
|
/// lifetime. The memory the pointer points to must be valid for as long as the font is
|
||||||
|
/// in use.
|
||||||
|
pub unsafe fn from_ptr(ptr: *const u16) -> FontGlyphRanges {
|
||||||
|
FontGlyphRanges(FontGlyphRangeData::Custom(ptr))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) unsafe fn to_ptr(&self, atlas: *mut sys::ImFontAtlas) -> *const sys::ImWchar {
|
||||||
|
match self.0 {
|
||||||
|
FontGlyphRangeData::ChineseFull => sys::ImFontAtlas_GetGlyphRangesChineseFull(atlas),
|
||||||
|
FontGlyphRangeData::ChineseSimplifiedCommon => {
|
||||||
|
sys::ImFontAtlas_GetGlyphRangesChineseSimplifiedCommon(atlas)
|
||||||
|
}
|
||||||
|
FontGlyphRangeData::Cyrillic => sys::ImFontAtlas_GetGlyphRangesCyrillic(atlas),
|
||||||
|
FontGlyphRangeData::Default => sys::ImFontAtlas_GetGlyphRangesDefault(atlas),
|
||||||
|
FontGlyphRangeData::Japanese => sys::ImFontAtlas_GetGlyphRangesJapanese(atlas),
|
||||||
|
FontGlyphRangeData::Korean => sys::ImFontAtlas_GetGlyphRangesKorean(atlas),
|
||||||
|
FontGlyphRangeData::Thai => sys::ImFontAtlas_GetGlyphRangesThai(atlas),
|
||||||
|
FontGlyphRangeData::Vietnamese => sys::ImFontAtlas_GetGlyphRangesVietnamese(atlas),
|
||||||
|
FontGlyphRangeData::Custom(ptr) => ptr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/fonts/mod.rs
Normal file
29
src/fonts/mod.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use crate::fonts::font::Font;
|
||||||
|
use crate::internal::RawCast;
|
||||||
|
use crate::Ui;
|
||||||
|
|
||||||
|
pub mod atlas;
|
||||||
|
pub mod font;
|
||||||
|
pub mod glyph;
|
||||||
|
pub mod glyph_ranges;
|
||||||
|
|
||||||
|
impl<'ui> Ui<'ui> {
|
||||||
|
/// Returns the current font
|
||||||
|
pub fn current_font(&self) -> &Font {
|
||||||
|
unsafe { Font::from_raw(&*sys::igGetFont()) }
|
||||||
|
}
|
||||||
|
/// Returns the current font size (= height in pixels) with font scale applied
|
||||||
|
pub fn current_font_size(&self) -> f32 {
|
||||||
|
unsafe { sys::igGetFontSize() }
|
||||||
|
}
|
||||||
|
/// Returns the UV coordinate for a white pixel.
|
||||||
|
///
|
||||||
|
/// Useful for drawing custom shapes with the draw list API.
|
||||||
|
pub fn font_tex_uv_white_pixel(&self) -> [f32; 2] {
|
||||||
|
unsafe { sys::igGetFontTexUvWhitePixel_nonUDT2().into() }
|
||||||
|
}
|
||||||
|
/// Set the font scale of the current window
|
||||||
|
pub fn set_window_font_scale(&self, scale: f32) {
|
||||||
|
unsafe { sys::igSetWindowFontScale(scale) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -170,3 +170,254 @@ impl<'ui> Ui<'ui> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mouse_down_clicked_released() {
|
||||||
|
for &button in MouseButton::VARIANTS.iter() {
|
||||||
|
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_down = [false; 5];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_down(button));
|
||||||
|
assert!(!ui.is_any_mouse_down());
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_released(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_down(button));
|
||||||
|
assert!(ui.is_any_mouse_down());
|
||||||
|
assert!(ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_released(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_down(button));
|
||||||
|
assert!(ui.is_any_mouse_down());
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_released(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = false;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_down(button));
|
||||||
|
assert!(!ui.is_any_mouse_down());
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(ui.is_mouse_released(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_down(button));
|
||||||
|
assert!(!ui.is_any_mouse_down());
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_released(button));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mouse_double_click() {
|
||||||
|
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||||
|
// Workaround for dear imgui bug/feature:
|
||||||
|
// If a button is clicked before io.mouse_double_click_time seconds has passed after the
|
||||||
|
// context is initialized, the single click is interpreted as a double-click. This happens
|
||||||
|
// because internally g.IO.MouseClickedTime is set to 0.0, so the context creation is
|
||||||
|
// considered a "click".
|
||||||
|
{
|
||||||
|
// Pass one second of time
|
||||||
|
ctx.io_mut().delta_time = 1.0;
|
||||||
|
let _ = ctx.frame();
|
||||||
|
}
|
||||||
|
// Fast clicks
|
||||||
|
ctx.io_mut().delta_time = 1.0 / 60.0;
|
||||||
|
for &button in MouseButton::VARIANTS.iter() {
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_down = [false; 5];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = false;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_clicked(button));
|
||||||
|
assert!(ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Slow clicks
|
||||||
|
ctx.io_mut().delta_time = 1.0;
|
||||||
|
for &button in MouseButton::VARIANTS.iter() {
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_down = [false; 5];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = false;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_clicked(button));
|
||||||
|
assert!(!ui.is_mouse_double_clicked(button));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_set_get_mouse_cursor() {
|
||||||
|
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||||
|
let ui = ctx.frame();
|
||||||
|
ui.set_mouse_cursor(None);
|
||||||
|
assert_eq!(None, ui.mouse_cursor());
|
||||||
|
ui.set_mouse_cursor(Some(MouseCursor::Hand));
|
||||||
|
assert_eq!(Some(MouseCursor::Hand), ui.mouse_cursor());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mouse_drags() {
|
||||||
|
for &button in MouseButton::VARIANTS.iter() {
|
||||||
|
let (_guard, mut ctx) = crate::test::test_ctx_initialized();
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_pos = [0.0, 0.0];
|
||||||
|
ctx.io_mut().mouse_down = [false; 5];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_dragging(button));
|
||||||
|
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [0.0, 0.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[0.0, 0.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_dragging(button));
|
||||||
|
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [0.0, 0.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[0.0, 0.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_pos = [0.0, 100.0];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_dragging(button));
|
||||||
|
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [0.0, 100.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[0.0, 0.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_pos = [0.0, 200.0];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_dragging(button));
|
||||||
|
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [0.0, 200.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[0.0, 200.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_pos = [10.0, 10.0];
|
||||||
|
ctx.io_mut()[button] = false;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_dragging(button));
|
||||||
|
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [10.0, 10.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[10.0, 10.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut()[button] = true;
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(!ui.is_mouse_dragging(button));
|
||||||
|
assert!(!ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [0.0, 0.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[0.0, 0.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_pos = [180.0, 180.0];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_dragging(button));
|
||||||
|
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [170.0, 170.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[170.0, 170.0]
|
||||||
|
);
|
||||||
|
ui.reset_mouse_drag_delta(button);
|
||||||
|
assert!(ui.is_mouse_dragging(button));
|
||||||
|
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [0.0, 0.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[0.0, 0.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
ctx.io_mut().mouse_pos = [200.0, 200.0];
|
||||||
|
let ui = ctx.frame();
|
||||||
|
assert!(ui.is_mouse_dragging(button));
|
||||||
|
assert!(ui.is_mouse_dragging_with_threshold(button, 200.0));
|
||||||
|
assert_eq!(ui.mouse_drag_delta(button), [20.0, 20.0]);
|
||||||
|
assert_eq!(
|
||||||
|
ui.mouse_drag_delta_with_threshold(button, 200.0),
|
||||||
|
[20.0, 20.0]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,14 +4,13 @@ use std::ops::{Index, IndexMut};
|
|||||||
use std::os::raw::{c_char, c_int, c_void};
|
use std::os::raw::{c_char, c_int, c_void};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use crate::fonts::atlas::FontAtlas;
|
||||||
|
use crate::fonts::font::Font;
|
||||||
use crate::input::keyboard::Key;
|
use crate::input::keyboard::Key;
|
||||||
use crate::input::mouse::MouseButton;
|
use crate::input::mouse::MouseButton;
|
||||||
use crate::internal::{ImVector, RawCast};
|
use crate::internal::{ImVector, RawCast};
|
||||||
use crate::sys;
|
use crate::sys;
|
||||||
|
|
||||||
type FontAtlas = sys::ImFontAtlas;
|
|
||||||
type Font = sys::ImFont;
|
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
/// Configuration flags
|
/// Configuration flags
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
|
use std::ffi::CStr;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
use crate::input::keyboard::Key;
|
use crate::input::keyboard::Key;
|
||||||
use crate::input::mouse::MouseButton;
|
use crate::input::mouse::MouseButton;
|
||||||
@ -173,17 +175,6 @@ bitflags!(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
bitflags!(
|
|
||||||
/// Flags for font atlases
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct ImFontAtlasFlags: c_int {
|
|
||||||
/// Don't round the height to next power of two
|
|
||||||
const NoPowerOfTwoHeight = 1;
|
|
||||||
/// Don't build software mouse cursors into the atlas
|
|
||||||
const NoMouseCursors = 1 << 1;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
bitflags!(
|
bitflags!(
|
||||||
/// Flags for hover checks
|
/// Flags for hover checks
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
@ -667,3 +658,11 @@ impl<'ui> Ui<'ui> {
|
|||||||
io.metrics_active_windows
|
io.metrics_active_windows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[deprecated(since = "0.1.0", note = "Use dear_imgui_version instead")]
|
||||||
|
pub fn get_version() -> &'static str {
|
||||||
|
unsafe {
|
||||||
|
let bytes = CStr::from_ptr(sys::igGetVersion()).to_bytes();
|
||||||
|
str::from_utf8_unchecked(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
62
src/lib.rs
62
src/lib.rs
@ -2,10 +2,10 @@ pub extern crate imgui_sys as sys;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
|
|
||||||
|
use std::cell;
|
||||||
use std::ffi::CStr;
|
use std::ffi::CStr;
|
||||||
use std::os::raw::{c_char, c_int, c_uchar, c_void};
|
use std::os::raw::{c_char, c_void};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -19,7 +19,10 @@ pub use self::drag::{
|
|||||||
DragFloat, DragFloat2, DragFloat3, DragFloat4, DragFloatRange2, DragInt, DragInt2, DragInt3,
|
DragFloat, DragFloat2, DragFloat3, DragFloat4, DragFloatRange2, DragInt, DragInt2, DragInt3,
|
||||||
DragInt4, DragIntRange2,
|
DragInt4, DragIntRange2,
|
||||||
};
|
};
|
||||||
pub use self::fonts::{FontGlyphRange, ImFont, ImFontAtlas, ImFontConfig};
|
pub use self::fonts::atlas::*;
|
||||||
|
pub use self::fonts::font::*;
|
||||||
|
pub use self::fonts::glyph::*;
|
||||||
|
pub use self::fonts::glyph_ranges::*;
|
||||||
pub use self::image::{Image, ImageButton};
|
pub use self::image::{Image, ImageButton};
|
||||||
pub use self::input::keyboard::*;
|
pub use self::input::keyboard::*;
|
||||||
pub use self::input::mouse::*;
|
pub use self::input::mouse::*;
|
||||||
@ -86,50 +89,7 @@ fn test_version() {
|
|||||||
assert_eq!(dear_imgui_version(), "1.71");
|
assert_eq!(dear_imgui_version(), "1.71");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TextureHandle<'a> {
|
|
||||||
pub width: u32,
|
|
||||||
pub height: u32,
|
|
||||||
pub pixels: &'a [c_uchar],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_version() -> &'static str {
|
|
||||||
unsafe {
|
|
||||||
let bytes = CStr::from_ptr(sys::igGetVersion()).to_bytes();
|
|
||||||
str::from_utf8_unchecked(bytes)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context {
|
impl Context {
|
||||||
pub fn fonts(&mut self) -> ImFontAtlas {
|
|
||||||
unsafe { ImFontAtlas::from_ptr(self.io_mut().fonts) }
|
|
||||||
}
|
|
||||||
pub fn prepare_texture<'a, F, T>(&mut self, f: F) -> T
|
|
||||||
where
|
|
||||||
F: FnOnce(TextureHandle<'a>) -> T,
|
|
||||||
{
|
|
||||||
let io = self.io();
|
|
||||||
let mut pixels: *mut c_uchar = ptr::null_mut();
|
|
||||||
let mut width: c_int = 0;
|
|
||||||
let mut height: c_int = 0;
|
|
||||||
let mut bytes_per_pixel: c_int = 0;
|
|
||||||
unsafe {
|
|
||||||
sys::ImFontAtlas_GetTexDataAsRGBA32(
|
|
||||||
io.fonts,
|
|
||||||
&mut pixels,
|
|
||||||
&mut width,
|
|
||||||
&mut height,
|
|
||||||
&mut bytes_per_pixel,
|
|
||||||
);
|
|
||||||
f(TextureHandle {
|
|
||||||
width: width as u32,
|
|
||||||
height: height as u32,
|
|
||||||
pixels: slice::from_raw_parts(pixels, (width * height * bytes_per_pixel) as usize),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn set_font_texture_id(&mut self, value: TextureId) {
|
|
||||||
self.fonts().set_texture_id(value.id());
|
|
||||||
}
|
|
||||||
pub fn get_time(&self) -> f64 {
|
pub fn get_time(&self) -> f64 {
|
||||||
unsafe { sys::igGetTime() }
|
unsafe { sys::igGetTime() }
|
||||||
}
|
}
|
||||||
@ -140,6 +100,7 @@ impl Context {
|
|||||||
|
|
||||||
pub struct Ui<'ui> {
|
pub struct Ui<'ui> {
|
||||||
ctx: &'ui Context,
|
ctx: &'ui Context,
|
||||||
|
font_atlas: Option<cell::RefMut<'ui, SharedFontAtlas>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
static FMT: &'static [u8] = b"%s\0";
|
static FMT: &'static [u8] = b"%s\0";
|
||||||
@ -152,6 +113,15 @@ impl<'ui> Ui<'ui> {
|
|||||||
pub fn io(&self) -> &Io {
|
pub fn io(&self) -> &Io {
|
||||||
unsafe { &*(sys::igGetIO() as *const Io) }
|
unsafe { &*(sys::igGetIO() as *const Io) }
|
||||||
}
|
}
|
||||||
|
pub fn fonts(&self) -> FontAtlasRef {
|
||||||
|
match self.font_atlas {
|
||||||
|
Some(ref font_atlas) => FontAtlasRef::Shared(font_atlas),
|
||||||
|
None => unsafe {
|
||||||
|
let fonts = &*(self.io().fonts as *const FontAtlas);
|
||||||
|
FontAtlasRef::Owned(fonts)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn get_time(&self) -> f64 {
|
pub fn get_time(&self) -> f64 {
|
||||||
unsafe { sys::igGetTime() }
|
unsafe { sys::igGetTime() }
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/test.rs
10
src/test.rs
@ -13,3 +13,13 @@ pub fn test_ctx() -> (ReentrantMutexGuard<'static, ()>, Context) {
|
|||||||
ctx.io_mut().ini_filename = ptr::null();
|
ctx.io_mut().ini_filename = ptr::null();
|
||||||
(guard, ctx)
|
(guard, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn test_ctx_initialized() -> (ReentrantMutexGuard<'static, ()>, Context) {
|
||||||
|
let (guard, mut ctx) = test_ctx();
|
||||||
|
let io = ctx.io_mut();
|
||||||
|
io.display_size = [1024.0, 768.0];
|
||||||
|
io.delta_time = 1.0 / 60.0;
|
||||||
|
io.mouse_pos = [0.0, 0.0];
|
||||||
|
ctx.fonts().build_rgba32_texture();
|
||||||
|
(guard, ctx)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user