pub struct Context { /* private fields */ }
Expand description
The main Thyme Context that holds internal PersistentState
and is responsible for creating Frames
.
This is created by build
on
ContextBuilder
after resource registration is complete.
Implementations§
source§impl Context
impl Context
sourcepub fn wants_mouse(&self) -> bool
pub fn wants_mouse(&self) -> bool
Returns true if thyme wants to use the mouse in the current frame, generally because the mouse is over a Thyme widget. If this returns true, you probably want Thyme to handle input this frame, while if it returns false, your application or game logic should handle input.
sourcepub fn wants_keyboard(&self) -> bool
pub fn wants_keyboard(&self) -> bool
Returns true if thyme wants to use keyboard input in the current frame, generally because a widget that accepts text input is keyboard focused. If this returns true, you probably don’t want to handle keyboard events in your own application code.
sourcepub fn mouse_time_in_current_widget(&self) -> u32
pub fn mouse_time_in_current_widget(&self) -> u32
Returns the amount of time, in milliseconds, that the mouse has been hovering
(inside) of the widget that it is currently inside. If hovered
is true
in a WidgetState
, then the mouse has been hovering
that widget for this amount of time.
sourcepub fn set_scale_factor(&mut self, scale: f32)
pub fn set_scale_factor(&mut self, scale: f32)
Sets the scale factor, sometimes referred to as HiDPI factor for the monitor.
This is normally handled by the IO
backend, which will set
the scale factor based on a scale factor changed event. User code should
not need to call this.
sourcepub fn scale_factor(&self) -> f32
pub fn scale_factor(&self) -> f32
Returns the current scale factor being used internally by Thyme. See
set_scale_factor
sourcepub fn set_display_size(&mut self, size: Point)
pub fn set_display_size(&mut self, size: Point)
Set the display size in logical pixels (physical pixels divided by the scale factor).
This is normally handled by the IO
backend, which will set
this in response to a window resize event. User code should
not need to call this.
sourcepub fn display_size(&self) -> Point
pub fn display_size(&self) -> Point
Returns the current display size being used internally by Thyme. See
set_display_size
sourcepub fn add_mouse_wheel(&mut self, delta: Point)
pub fn add_mouse_wheel(&mut self, delta: Point)
Add mouse wheel event, with delta
being the amount of device-dependant logical scrolling.
This is normally handled by the IO
backend, which will set
this in response to a window resize event. User code should
not need to call this.
sourcepub fn set_input_modifiers(&mut self, input_modifiers: InputModifiers)
pub fn set_input_modifiers(&mut self, input_modifiers: InputModifiers)
Set the input modifiers - the status of keys such as ctrl
and shift
.
This is normally handled by the IO
backend, which will set
this in response to a window resize event. User code should
not need to call this.
sourcepub fn set_mouse_pressed(&mut self, pressed: bool, index: usize)
pub fn set_mouse_pressed(&mut self, pressed: bool, index: usize)
sourcepub fn push_character(&mut self, c: char)
pub fn push_character(&mut self, c: char)
Pushes a character (that was received from the keyboard) to thyme, to be
dispatched to the appropriate widget based on keyboard focus in the next Frame.
This is normally handled by the IO
backend, which will set
this in response to a window resize event. User code should
not need to call this.
sourcepub fn mouse_pos(&self) -> Point
pub fn mouse_pos(&self) -> Point
Returns the current mouse position, based on mouse cursor movement. The scale factor must be taken into account to convert physical pixels to the logical pixels used by this. This may be useful is you want to get Thyme’s last mouse position outside of a Thyme frame for the rest of your application to use.
sourcepub fn set_mouse_pos(&mut self, pos: Point)
pub fn set_mouse_pos(&mut self, pos: Point)
Set mouse position, based on mouse cursor movement. The scale factor must
be taken into account to convert physical pixels to the logical pixels used by
this.
This is normally handled by the IO
backend, which will set
this in response to a window resize event. User code should
not need to call this.
sourcepub fn add_theme_file<P: Into<PathBuf>>(&mut self, path: P)
pub fn add_theme_file<P: Into<PathBuf>>(&mut self, path: P)
Adds the specified path as a source file for the resources being used
by the theme for this context. This will only work if the theme was
set up to read source data from files, i.e. using
ContextBuilder#register_theme_from_files
This does not rebuild the theme; you will
need to call rebuild_all
for that.
Examples found in repository?
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
pub fn check_context_changes<R: Renderer>(&mut self, context: &mut Context, renderer: &mut R) {
if let Some(old_choice) = self.old_theme_choice.take() {
context.remove_theme_file(old_choice.path());
context.add_theme_file(self.theme_choice.path());
}
if self.reload_assets {
if let Err(e) = context.rebuild_all(renderer) {
log::error!("Unable to rebuild theme: {}", e);
}
self.reload_assets = false;
} else if !self.live_reload_disabled {
if let Err(e) = context.check_live_reload(renderer) {
log::error!("Unable to live reload theme: {}", e);
}
}
}
sourcepub fn remove_theme_file<P: Into<PathBuf>>(&mut self, path: P)
pub fn remove_theme_file<P: Into<PathBuf>>(&mut self, path: P)
Removes the theme source file with the specified path from the resources
being used by the theme for this context, if it is present. If it is not
present, does nothing. This does not rebuild the theme; you will
need to call rebuild_all
for that.
Examples found in repository?
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
pub fn check_context_changes<R: Renderer>(&mut self, context: &mut Context, renderer: &mut R) {
if let Some(old_choice) = self.old_theme_choice.take() {
context.remove_theme_file(old_choice.path());
context.add_theme_file(self.theme_choice.path());
}
if self.reload_assets {
if let Err(e) = context.rebuild_all(renderer) {
log::error!("Unable to rebuild theme: {}", e);
}
self.reload_assets = false;
} else if !self.live_reload_disabled {
if let Err(e) = context.check_live_reload(renderer) {
log::error!("Unable to live reload theme: {}", e);
}
}
}
sourcepub fn rebuild_all<R: Renderer>(
&mut self,
renderer: &mut R
) -> Result<(), Error>
pub fn rebuild_all<R: Renderer>( &mut self, renderer: &mut R ) -> Result<(), Error>
Rebuilds this context, reloading all asset data. Notably, files on disk
that were used in building
the context
are re-read. If any errors are encountered in reading or parsing files, this
will return Err
and no changes are made to the context.
Examples found in repository?
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
pub fn check_context_changes<R: Renderer>(&mut self, context: &mut Context, renderer: &mut R) {
if let Some(old_choice) = self.old_theme_choice.take() {
context.remove_theme_file(old_choice.path());
context.add_theme_file(self.theme_choice.path());
}
if self.reload_assets {
if let Err(e) = context.rebuild_all(renderer) {
log::error!("Unable to rebuild theme: {}", e);
}
self.reload_assets = false;
} else if !self.live_reload_disabled {
if let Err(e) = context.check_live_reload(renderer) {
log::error!("Unable to live reload theme: {}", e);
}
}
}
sourcepub fn check_live_reload<R: Renderer>(
&mut self,
renderer: &mut R
) -> Result<(), Error>
pub fn check_live_reload<R: Renderer>( &mut self, renderer: &mut R ) -> Result<(), Error>
Checks the internal live reload thread to see if any file notifications have occurred
since the last check. If so, will fully rebuild the theme. If any errors are encountered
in the process of rebuilding the theme, will return the Err
and no changes are made to
the current theme. Note that if you built the context with live reload disabled
(see BuildOptions
), this function will do nothing.
Examples found in repository?
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
pub fn check_context_changes<R: Renderer>(&mut self, context: &mut Context, renderer: &mut R) {
if let Some(old_choice) = self.old_theme_choice.take() {
context.remove_theme_file(old_choice.path());
context.add_theme_file(self.theme_choice.path());
}
if self.reload_assets {
if let Err(e) = context.rebuild_all(renderer) {
log::error!("Unable to rebuild theme: {}", e);
}
self.reload_assets = false;
} else if !self.live_reload_disabled {
if let Err(e) = context.check_live_reload(renderer) {
log::error!("Unable to live reload theme: {}", e);
}
}
}
sourcepub fn save(&self) -> SavedContext
pub fn save(&self) -> SavedContext
Generates a SavedContext
from the current
context state. This can be serialized to a file and restored later using
load
to restore the Context state.
sourcepub fn load(&mut self, save: SavedContext)
pub fn load(&mut self, save: SavedContext)
Restores the specified SavedContext
to this
Context, restoring the overall UI state. The SavedContext
passed in should be generated from save
.
sourcepub fn create_frame(&mut self) -> Frame
pub fn create_frame(&mut self) -> Frame
Creates a Frame
, the main object that should pass through
your UI building functions and is responsible for constructing the widget tree.
This method should be called each frame you want to draw / interact with the UI.
Examples found in repository?
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn main() -> Result<(), Box<dyn std::error::Error>> {
use glium::glutin::{window::WindowBuilder};
use glium::{Display, Surface};
// initialize our very basic logger so error messages go to stdout
thyme::log::init(log::Level::Warn).unwrap();
let window_size = [1280.0, 720.0];
let events_loop = EventLoop::new();
// create glium display
let builder = WindowBuilder::new()
.with_title("Thyme Demo")
.with_inner_size(glium::glutin::dpi::LogicalSize::new(window_size[0], window_size[1]));
let context = glium::glutin::ContextBuilder::new();
let display = Display::new(builder, context, &events_loop)?;
// create thyme backend
let mut renderer = thyme::GliumRenderer::new(&display)?;
let mut io = thyme::WinitIo::new(&events_loop, window_size.into())?;
let mut context_builder = thyme::ContextBuilder::with_defaults();
demo::register_assets(&mut context_builder);
let mut context = context_builder.build(&mut renderer, &mut io)?;
let mut party = demo::Party::default();
let mut last_frame = std::time::Instant::now();
let frame_time = std::time::Duration::from_millis(16);
// run main loop
events_loop.run(move |event, _, control_flow| match event {
Event::MainEventsCleared => {
if std::time::Instant::now() > last_frame + frame_time {
display.gl_window().window().request_redraw();
}
*control_flow = ControlFlow::WaitUntil(last_frame + frame_time);
},
Event::RedrawRequested(_) => {
last_frame = std::time::Instant::now();
party.check_context_changes(&mut context, &mut renderer);
let mut target = display.draw();
target.clear_color(0.21404, 0.21404, 0.21404, 1.0); // manual sRGB conversion for 0.5
bench::run("thyme", || {
display.gl_window().window().set_cursor_visible(!party.theme_has_mouse_cursor());
let mut ui = context.create_frame();
bench::run("frame", || {
demo::build_ui(&mut ui, &mut party);
});
bench::run("draw", || {
renderer.draw_frame(&mut target, ui).unwrap();
});
});
target.finish().unwrap();
},
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit,
event => {
io.handle_event(&mut context, &event);
}
})
}
More examples
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
fn main() -> Result<(), Box<dyn std::error::Error>> {
// initialize our very basic logger so error messages go to stdout
thyme::log::init(log::Level::Warn).unwrap();
// create glium display
let event_loop = glutin::event_loop::EventLoop::new();
let window_builder = glutin::window::WindowBuilder::new()
.with_title("Hello world!")
.with_inner_size(glutin::dpi::LogicalSize::new(1280.0, 720.0));
let windowed_context = glutin::ContextBuilder::new()
.with_gl(glutin::GlRequest::Specific(
glutin::Api::OpenGl,
(OPENGL_MAJOR_VERSION, OPENGL_MINOR_VERSION),
))
.build_windowed(window_builder, &event_loop)?;
let windowed_context = unsafe {
windowed_context
.make_current().map_err(|(_context, e)| e)?
};
{
let gl_context = windowed_context.context();
gl::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _)
}
// create thyme backend
let mut renderer = thyme::GLRenderer::new();
let mut context_builder = thyme::ContextBuilder::with_defaults();
demo::register_assets(&mut context_builder);
let window_size = [1280.0, 720.0];
let mut io = thyme::WinitIo::new(&event_loop, window_size.into())?;
let mut context = context_builder.build(&mut renderer, &mut io)?;
let mut party = demo::Party::default();
let mut last_frame = std::time::Instant::now();
let frame_time = std::time::Duration::from_millis(16);
// run main loop
event_loop.run(move |event, _, control_flow| match event {
Event::MainEventsCleared => {
if std::time::Instant::now() > last_frame + frame_time {
windowed_context.window().request_redraw();
}
*control_flow = ControlFlow::WaitUntil(last_frame + frame_time);
}
Event::RedrawRequested(_) => {
last_frame = std::time::Instant::now();
party.check_context_changes(&mut context, &mut renderer);
renderer.clear_color(0.5, 0.5, 0.5, 1.0);
bench::run("thyme", || {
windowed_context.window().set_cursor_visible(!party.theme_has_mouse_cursor());
let mut ui = context.create_frame();
bench::run("frame", || {
demo::build_ui(&mut ui, &mut party);
});
bench::run("draw", || {
renderer.draw_frame(ui);
});
});
windowed_context.swap_buffers().unwrap();
}
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
event => {
io.handle_event(&mut context, &event);
}
})
}
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
fn main() -> Result<(), Box<dyn std::error::Error>> {
use winit::{ window::WindowBuilder };
// initialize our very basic logger so error messages go to stdout
thyme::log::init(log::Level::Warn).unwrap();
let window_size = [1280.0, 720.0];
let events_loop = EventLoop::new();
// create winit window
let window = WindowBuilder::new()
.with_title("Thyme WGPU Demo")
.with_inner_size(winit::dpi::LogicalSize::new(window_size[0], window_size[1]))
.build(&events_loop)?;
// setup WGPU
let instance_desc = wgpu::InstanceDescriptor {
backends: wgpu::Backends::PRIMARY,
dx12_shader_compiler: wgpu::Dx12Compiler::Fxc,
};
let instance = wgpu::Instance::new(instance_desc);
let surface = unsafe { instance.create_surface(&window).map_err(thyme::Error::WgpuSurface)? };
let (_adapter, device, queue) = futures::executor::block_on(setup_wgpu(&instance, &surface));
let surface_config = get_surface_config(window_size[0] as u32, window_size[1] as u32);
surface.configure(&device, &surface_config);
// create thyme backend
let mut renderer = thyme::WgpuRenderer::new(Arc::clone(&device), Arc::clone(&queue));
let mut io = thyme::WinitIo::new(&events_loop, window_size.into())?;
let mut context_builder = thyme::ContextBuilder::with_defaults();
demo::register_assets(&mut context_builder);
let mut context = context_builder.build(&mut renderer, &mut io)?;
let mut party = demo::Party::default();
let mut last_frame = std::time::Instant::now();
let frame_time = std::time::Duration::from_millis(16);
// run main loop
events_loop.run(move |event, _, control_flow| match event {
Event::MainEventsCleared => {
if std::time::Instant::now() > last_frame + frame_time {
window.request_redraw();
}
*control_flow = ControlFlow::WaitUntil(last_frame + frame_time);
},
Event::RedrawRequested(_) => {
last_frame = std::time::Instant::now();
party.check_context_changes(&mut context, &mut renderer);
let frame = surface.get_current_texture().unwrap();
let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
bench::run("thyme", || {
window.set_cursor_visible(!party.theme_has_mouse_cursor());
let mut ui = context.create_frame();
bench::run("frame", || {
demo::build_ui(&mut ui, &mut party);
});
bench::run("draw", || {
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }),
store: true,
},
})],
depth_stencil_attachment: None,
});
renderer.draw_frame(ui, &mut render_pass);
}
queue.submit(Some(encoder.finish()));
frame.present();
});
});
},
Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit,
event => {
// recreate swap chain on resize, but also still pass the event to thyme
if let Event::WindowEvent { event: WindowEvent::Resized(_), ..} = event {
let size: (u32, u32) = window.inner_size().into();
let surface_config = get_surface_config(size.0, size.1);
surface.configure(&device, &surface_config);
}
io.handle_event(&mut context, &event);
}
})
}