mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-11 05:28:35 +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;
|
||||
|
||||
const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
struct State {
|
||||
example: i32,
|
||||
notify_text: &'static str,
|
||||
@ -25,15 +23,15 @@ impl Default for State {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let system = support::init(file!());
|
||||
let mut state = State::default();
|
||||
support::run("color_button.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
||||
system.main_loop(|_, ui| {
|
||||
example_selector(&mut state, ui);
|
||||
match state.example {
|
||||
1 => example_1(&mut state, ui),
|
||||
2 => example_2(ui),
|
||||
_ => (),
|
||||
}
|
||||
true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -12,9 +12,6 @@ use image::{jpeg::JPEGDecoder, ImageDecoder};
|
||||
use imgui::*;
|
||||
|
||||
mod support;
|
||||
use self::support::Textures;
|
||||
|
||||
const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
#[derive(Default)]
|
||||
struct CustomTexturesApp {
|
||||
@ -31,7 +28,7 @@ impl CustomTexturesApp {
|
||||
fn register_textures<F>(
|
||||
&mut self,
|
||||
gl_ctx: &F,
|
||||
textures: &mut Textures,
|
||||
textures: &mut Textures<Rc<Texture2d>>,
|
||||
) -> Result<(), Box<dyn Error>>
|
||||
where
|
||||
F: Facade,
|
||||
@ -89,7 +86,7 @@ impl CustomTexturesApp {
|
||||
}
|
||||
|
||||
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
|
||||
F: Facade,
|
||||
{
|
||||
@ -121,16 +118,9 @@ impl Lenna {
|
||||
fn main() {
|
||||
let mut my_app = CustomTexturesApp::default();
|
||||
|
||||
support::run(
|
||||
"custom_textures.rs".to_owned(),
|
||||
CLEAR_COLOR,
|
||||
|ui, gl_ctx, textures| {
|
||||
if let Err(e) = my_app.register_textures(gl_ctx, textures) {
|
||||
panic!("Failed to register textures! {}", e);
|
||||
}
|
||||
my_app.show_textures(ui);
|
||||
|
||||
true
|
||||
},
|
||||
);
|
||||
let mut system = support::init(file!());
|
||||
my_app
|
||||
.register_textures(system.display.get_context(), system.renderer.textures())
|
||||
.expect("Failed to register textures");
|
||||
system.main_loop(|_, ui| my_app.show_textures(ui));
|
||||
}
|
||||
|
||||
@ -2,29 +2,21 @@ use imgui::*;
|
||||
|
||||
mod support;
|
||||
|
||||
const CLEAR_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
fn main() {
|
||||
support::run("hello_world.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
||||
hello_world(ui)
|
||||
let system = support::init(file!());
|
||||
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::{
|
||||
backend::{Context, Facade},
|
||||
Texture2d,
|
||||
};
|
||||
use imgui::{self, FontGlyphRange, ImFontConfig, Ui};
|
||||
use glium::glutin::{self, Event, WindowEvent};
|
||||
use glium::{Display, Surface};
|
||||
use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui};
|
||||
use imgui_glium_renderer::GliumRenderer;
|
||||
use imgui_winit_support::{HiDpiMode, WinitPlatform};
|
||||
use std::rc::Rc;
|
||||
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)
|
||||
where
|
||||
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();
|
||||
pub fn init(title: &str) -> System {
|
||||
let events_loop = glutin::EventsLoop::new();
|
||||
let context = glutin::ContextBuilder::new().with_vsync(true);
|
||||
let builder = glutin::WindowBuilder::new()
|
||||
.with_title(title)
|
||||
.with_title(title.to_owned())
|
||||
.with_dimensions(glutin::dpi::LogicalSize::new(1024f64, 768f64));
|
||||
let display = Display::new(builder, context, &events_loop).unwrap();
|
||||
let gl_window = display.gl_window();
|
||||
let window = gl_window.window();
|
||||
let display =
|
||||
Display::new(builder, context, &events_loop).expect("Failed to initialize display");
|
||||
|
||||
let mut imgui = imgui::Context::create();
|
||||
let mut imgui = Context::create();
|
||||
imgui.set_ini_filename(None);
|
||||
|
||||
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 font_size = (13.0 * hidpi_factor) as f32;
|
||||
|
||||
imgui.fonts().add_default_font_with_config(
|
||||
ImFontConfig::new()
|
||||
.oversample_h(1)
|
||||
.pixel_snap_h(true)
|
||||
.size_pixels(font_size),
|
||||
);
|
||||
|
||||
imgui.fonts().add_font_with_config(
|
||||
include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
||||
ImFontConfig::new()
|
||||
.merge_mode(true)
|
||||
.oversample_h(1)
|
||||
.pixel_snap_h(true)
|
||||
.size_pixels(font_size)
|
||||
.rasterizer_multiply(1.75),
|
||||
&FontGlyphRange::japanese(),
|
||||
);
|
||||
imgui.fonts().add_font(&[
|
||||
FontSource::DefaultFontData {
|
||||
config: Some(FontConfig {
|
||||
size_pixels: font_size,
|
||||
..FontConfig::default()
|
||||
}),
|
||||
},
|
||||
FontSource::TtfData {
|
||||
data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
||||
size_pixels: font_size,
|
||||
config: Some(FontConfig {
|
||||
rasterizer_multiply: 1.75,
|
||||
glyph_ranges: FontGlyphRanges::japanese(),
|
||||
..FontConfig::default()
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
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");
|
||||
|
||||
let mut last_frame = Instant::now();
|
||||
let mut quit = false;
|
||||
System {
|
||||
events_loop,
|
||||
display,
|
||||
imgui,
|
||||
platform,
|
||||
renderer,
|
||||
font_size,
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
events_loop.poll_events(|event| {
|
||||
use glium::glutin::{Event, WindowEvent::CloseRequested};
|
||||
impl System {
|
||||
pub fn main_loop<F: FnMut(&mut bool, &mut Ui)>(self, mut run_ui: F) {
|
||||
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 {
|
||||
match event {
|
||||
CloseRequested => quit = true,
|
||||
_ => (),
|
||||
if let Event::WindowEvent { event, .. } = event {
|
||||
if let WindowEvent::CloseRequested = event {
|
||||
run = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let io = imgui.io_mut();
|
||||
platform
|
||||
.prepare_frame(io, &window)
|
||||
.expect("Failed to start frame");
|
||||
last_frame = io.update_delta_time(last_frame);
|
||||
let ui = imgui.frame();
|
||||
if !run_ui(&ui, display.get_context(), renderer.textures()) {
|
||||
break;
|
||||
}
|
||||
let io = imgui.io_mut();
|
||||
platform
|
||||
.prepare_frame(io, &window)
|
||||
.expect("Failed to start frame");
|
||||
last_frame = io.update_delta_time(last_frame);
|
||||
let mut ui = imgui.frame();
|
||||
run_ui(&mut run, &mut ui);
|
||||
|
||||
let mut target = display.draw();
|
||||
target.clear_color(
|
||||
clear_color[0],
|
||||
clear_color[1],
|
||||
clear_color[2],
|
||||
clear_color[3],
|
||||
);
|
||||
platform.prepare_render(&ui, &window);
|
||||
renderer.render(&mut target, ui).expect("Rendering failed");
|
||||
target.finish().unwrap();
|
||||
|
||||
if quit {
|
||||
break;
|
||||
let mut target = display.draw();
|
||||
target.clear_color_srgb(1.0, 1.0, 1.0, 1.0);
|
||||
platform.prepare_render(&ui, &window);
|
||||
renderer.render(&mut target, ui).expect("Rendering failed");
|
||||
target.finish().expect("Failed to swap buffers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,43 +1,38 @@
|
||||
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 RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
|
||||
|
||||
fn main() {
|
||||
support::run(
|
||||
"test_drawing_channels_split".to_owned(),
|
||||
CLEAR_COLOR,
|
||||
|ui, _, _| {
|
||||
let draw_list = ui.get_window_draw_list();
|
||||
// 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
|
||||
// the white circle.
|
||||
draw_list.channels_split(2, |channels| {
|
||||
const RADIUS: f32 = 100.0;
|
||||
let canvas_pos = ui.get_cursor_screen_pos();
|
||||
channels.set_current(1);
|
||||
draw_list
|
||||
.add_line(
|
||||
canvas_pos,
|
||||
[canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS],
|
||||
RED,
|
||||
)
|
||||
.thickness(5.0)
|
||||
.build();
|
||||
let system = support::init(file!());
|
||||
system.main_loop(|_, ui| {
|
||||
let draw_list = ui.get_window_draw_list();
|
||||
// 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
|
||||
// the white circle.
|
||||
draw_list.channels_split(2, |channels| {
|
||||
const RADIUS: f32 = 100.0;
|
||||
let canvas_pos = ui.get_cursor_screen_pos();
|
||||
channels.set_current(1);
|
||||
draw_list
|
||||
.add_line(
|
||||
canvas_pos,
|
||||
[canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS],
|
||||
RED,
|
||||
)
|
||||
.thickness(5.0)
|
||||
.build();
|
||||
|
||||
channels.set_current(0);
|
||||
let center = [canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS];
|
||||
draw_list
|
||||
.add_circle(center, RADIUS, WHITE)
|
||||
.thickness(10.0)
|
||||
.num_segments(50)
|
||||
.build();
|
||||
});
|
||||
true
|
||||
},
|
||||
);
|
||||
channels.set_current(0);
|
||||
let center = [canvas_pos[0] + RADIUS, canvas_pos[1] + RADIUS];
|
||||
draw_list
|
||||
.add_circle(center, RADIUS, WHITE)
|
||||
.thickness(10.0)
|
||||
.num_segments(50)
|
||||
.build();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,11 +1,6 @@
|
||||
mod support;
|
||||
|
||||
const CLEAR_COLOR: [f32; 4] = [0.2, 0.2, 0.2, 1.0];
|
||||
|
||||
fn main() {
|
||||
support::run("test_window.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
||||
let mut open = true;
|
||||
ui.show_demo_window(&mut open);
|
||||
open
|
||||
});
|
||||
let system = support::init(file!());
|
||||
system.main_loop(|run, ui| ui.show_demo_window(run));
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
let mut state = State::default();
|
||||
|
||||
support::run("test_window.rs".to_owned(), CLEAR_COLOR, |ui, _, _| {
|
||||
let mut open = true;
|
||||
show_test_window(ui, &mut state, &mut open);
|
||||
open
|
||||
});
|
||||
let system = support::init(file!());
|
||||
system.main_loop(|run, ui| show_test_window(ui, &mut state, run));
|
||||
}
|
||||
|
||||
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 glutin::{Event, WindowEvent};
|
||||
use imgui::{FontGlyphRange, ImFontConfig, Context, Ui};
|
||||
use imgui::{FontGlyphRanges, FontConfig, FontSource, Context, Ui};
|
||||
use imgui_gfx_renderer::{GfxRenderer, Shaders};
|
||||
use imgui_winit_support::{WinitPlatform, HiDpiMode};
|
||||
use std::time::Instant;
|
||||
|
||||
type ColorFormat = gfx::format::Rgba8;
|
||||
type DepthFormat = gfx::format::DepthStencil;
|
||||
|
||||
#[cfg(feature = "opengl")]
|
||||
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 context = glutin::ContextBuilder::new().with_vsync(true);
|
||||
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));
|
||||
let (windowed_context, mut device, mut factory, mut main_color, mut main_depth) =
|
||||
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 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 font_size = (13.0 * hidpi_factor) as f32;
|
||||
|
||||
imgui.fonts().add_default_font_with_config(
|
||||
ImFontConfig::new()
|
||||
.oversample_h(1)
|
||||
.pixel_snap_h(true)
|
||||
.size_pixels(font_size),
|
||||
);
|
||||
|
||||
imgui.fonts().add_font_with_config(
|
||||
include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
||||
ImFontConfig::new()
|
||||
.merge_mode(true)
|
||||
.oversample_h(1)
|
||||
.pixel_snap_h(true)
|
||||
.size_pixels(font_size)
|
||||
.rasterizer_multiply(1.75),
|
||||
&FontGlyphRange::japanese(),
|
||||
);
|
||||
imgui.fonts().add_font(&[
|
||||
FontSource::DefaultFontData {
|
||||
config: Some(FontConfig {
|
||||
size_pixels: font_size,
|
||||
..FontConfig::default()
|
||||
}),
|
||||
},
|
||||
FontSource::TtfData {
|
||||
data: include_bytes!("../../../resources/mplus-1p-regular.ttf"),
|
||||
size_pixels: font_size,
|
||||
config: Some(FontConfig {
|
||||
rasterizer_multiply: 1.75,
|
||||
glyph_ranges: FontGlyphRanges::japanese(),
|
||||
..FontConfig::default()
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
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 glutin;
|
||||
|
||||
type ColorFormat = gfx::format::Rgba8;
|
||||
type DepthFormat = gfx::format::DepthStencil;
|
||||
|
||||
let mut events_loop = glutin::EventsLoop::new();
|
||||
let window = glutin::WindowBuilder::new()
|
||||
.with_title(title)
|
||||
|
||||
@ -136,21 +136,7 @@ where
|
||||
gfx::memory::Usage::Dynamic,
|
||||
Bind::empty(),
|
||||
)?;
|
||||
let (_, texture) = ctx.prepare_texture(|handle| {
|
||||
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 font_texture = upload_font_texture(ctx.fonts(), factory)?;
|
||||
let slice = Slice {
|
||||
start: 0,
|
||||
end: 0,
|
||||
@ -167,12 +153,20 @@ where
|
||||
index_buffer,
|
||||
slice,
|
||||
pso,
|
||||
font_texture: (texture, sampler),
|
||||
font_texture,
|
||||
textures: Textures::new(),
|
||||
#[cfg(feature = "directx")]
|
||||
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>> {
|
||||
&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")]
|
||||
mod constants {
|
||||
use gfx::gfx_constant_struct_meta;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
use glium::backend::{Context, Facade};
|
||||
use glium::index::{self, PrimitiveType};
|
||||
use glium::program::ProgramChooserCreationError;
|
||||
use glium::texture::{ClientFormat, RawImage2d, TextureCreationError};
|
||||
use glium::texture::{ClientFormat, MipmapsOption, RawImage2d, TextureCreationError};
|
||||
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter};
|
||||
use glium::{
|
||||
program, uniform, vertex, Blend, DrawError, DrawParameters, IndexBuffer, Program, Rect,
|
||||
@ -81,16 +81,7 @@ impl GliumRenderer {
|
||||
facade: &F,
|
||||
) -> Result<GliumRenderer, GliumRendererError> {
|
||||
let program = compile_default_program(facade)?;
|
||||
let font_texture = ctx.prepare_texture(|handle| {
|
||||
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));
|
||||
let font_texture = upload_font_texture(ctx.fonts(), facade.get_context())?;
|
||||
ctx.set_renderer_name(Some(ImString::from(format!(
|
||||
"imgui-glium-renderer {}",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
@ -102,6 +93,13 @@ impl GliumRenderer {
|
||||
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>> {
|
||||
&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> {
|
||||
program!(
|
||||
facade,
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
use parking_lot::ReentrantMutex;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::CStr;
|
||||
use std::ops::Drop;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::fonts::atlas::{FontAtlas, FontAtlasRefMut, FontId, SharedFontAtlas};
|
||||
use crate::io::Io;
|
||||
use crate::string::{ImStr, ImString};
|
||||
use crate::style::Style;
|
||||
@ -46,6 +49,7 @@ use crate::Ui;
|
||||
#[derive(Debug)]
|
||||
pub struct Context {
|
||||
raw: *mut sys::ImGuiContext,
|
||||
shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>,
|
||||
ini_filename: Option<ImString>,
|
||||
log_filename: Option<ImString>,
|
||||
platform_name: Option<ImString>,
|
||||
@ -75,7 +79,15 @@ impl Context {
|
||||
///
|
||||
/// Panics if an active context already exists
|
||||
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.
|
||||
pub fn suspend(self) -> SuspendedContext {
|
||||
@ -158,7 +170,7 @@ impl Context {
|
||||
let data = unsafe { CStr::from_ptr(sys::igSaveIniSettingsToMemory(ptr::null_mut())) };
|
||||
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();
|
||||
assert!(
|
||||
no_current_context(),
|
||||
@ -169,6 +181,7 @@ impl Context {
|
||||
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
||||
Context {
|
||||
raw,
|
||||
shared_font_atlas,
|
||||
ini_filename: None,
|
||||
log_filename: None,
|
||||
platform_name: None,
|
||||
@ -217,7 +230,11 @@ pub struct SuspendedContext(Context);
|
||||
impl SuspendedContext {
|
||||
/// Creates a new suspended imgui-rs context.
|
||||
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.
|
||||
///
|
||||
@ -236,11 +253,12 @@ impl SuspendedContext {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
fn create_internal() -> Self {
|
||||
fn create_internal(shared_font_atlas: Option<Rc<RefCell<SharedFontAtlas>>>) -> Self {
|
||||
let _guard = CTX_MUTEX.lock();
|
||||
let raw = unsafe { sys::igCreateContext(ptr::null_mut()) };
|
||||
let ctx = Context {
|
||||
raw,
|
||||
shared_font_atlas,
|
||||
ini_filename: None,
|
||||
log_filename: None,
|
||||
platform_name: None,
|
||||
@ -323,6 +341,31 @@ fn test_suspend_failure() {
|
||||
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]
|
||||
fn test_ini_load_save() {
|
||||
let (_guard, mut ctx) = crate::test::test_ctx();
|
||||
@ -365,10 +408,42 @@ impl Context {
|
||||
&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> {
|
||||
// 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 {
|
||||
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::time::Instant;
|
||||
|
||||
use crate::fonts::atlas::FontAtlas;
|
||||
use crate::fonts::font::Font;
|
||||
use crate::input::keyboard::Key;
|
||||
use crate::input::mouse::MouseButton;
|
||||
use crate::internal::{ImVector, RawCast};
|
||||
use crate::sys;
|
||||
|
||||
type FontAtlas = sys::ImFontAtlas;
|
||||
type Font = sys::ImFont;
|
||||
|
||||
bitflags! {
|
||||
/// Configuration flags
|
||||
#[repr(transparent)]
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
#![allow(non_upper_case_globals)]
|
||||
use bitflags::bitflags;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::c_int;
|
||||
use std::str;
|
||||
|
||||
use crate::input::keyboard::Key;
|
||||
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!(
|
||||
/// Flags for hover checks
|
||||
#[repr(C)]
|
||||
@ -667,3 +658,11 @@ impl<'ui> Ui<'ui> {
|
||||
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]
|
||||
extern crate lazy_static;
|
||||
|
||||
use std::cell;
|
||||
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::slice;
|
||||
use std::str;
|
||||
use std::thread;
|
||||
|
||||
@ -19,7 +19,10 @@ pub use self::drag::{
|
||||
DragFloat, DragFloat2, DragFloat3, DragFloat4, DragFloatRange2, DragInt, DragInt2, DragInt3,
|
||||
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::input::keyboard::*;
|
||||
pub use self::input::mouse::*;
|
||||
@ -86,50 +89,7 @@ fn test_version() {
|
||||
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 {
|
||||
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 {
|
||||
unsafe { sys::igGetTime() }
|
||||
}
|
||||
@ -140,6 +100,7 @@ impl Context {
|
||||
|
||||
pub struct Ui<'ui> {
|
||||
ctx: &'ui Context,
|
||||
font_atlas: Option<cell::RefMut<'ui, SharedFontAtlas>>,
|
||||
}
|
||||
|
||||
static FMT: &'static [u8] = b"%s\0";
|
||||
@ -152,6 +113,15 @@ impl<'ui> Ui<'ui> {
|
||||
pub fn io(&self) -> &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 {
|
||||
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();
|
||||
(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