Pull font API and associated refactoring from 0.1-dev

This commit is contained in:
Joonas Javanainen 2019-06-29 12:43:49 +03:00
parent f60c597d11
commit cebe02cb11
No known key found for this signature in database
GPG Key ID: D39CCA5CB19B9179
24 changed files with 1313 additions and 717 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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");
}
}
}

View File

@ -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();
});
});
}

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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]
);
}
}
}

View File

@ -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)]

View File

@ -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)
}
}

View File

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

View File

@ -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)
}