mirror of
https://github.com/emilk/egui.git
synced 2026-06-26 22:53:14 -04:00
Merge branch 'main' into ime-preedit-visuals
This commit is contained in:
25
.github/workflows/taplo.yml
vendored
Normal file
25
.github/workflows/taplo.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
# Checks that all TOML files are formatted with taplo.
|
||||
name: Taplo
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
taplo:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Taplo
|
||||
uses: taiki-e/install-action@v2.48.7
|
||||
with:
|
||||
tool: taplo-cli@0.9.3
|
||||
|
||||
- name: Check TOML formatting
|
||||
run: |
|
||||
taplo fmt --check
|
||||
@@ -1337,7 +1337,7 @@ dependencies = [
|
||||
"image",
|
||||
"jiff",
|
||||
"mimalloc",
|
||||
"rand 0.9.2",
|
||||
"rand 0.9.3",
|
||||
"serde",
|
||||
"unicode_names2",
|
||||
]
|
||||
@@ -3657,9 +3657,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
|
||||
@@ -134,12 +134,16 @@ syntect = { version = "5.3.0", default-features = false }
|
||||
tempfile = "3.23.0"
|
||||
thiserror = "2.0.17"
|
||||
tokio = "1.49"
|
||||
toml = {version = "1.0.0", default-features = false }
|
||||
toml = { version = "1.0.0", default-features = false }
|
||||
type-map = "0.5.1"
|
||||
unicode_names2 = { version = "2.0.0", default-features = false }
|
||||
unicode-general-category = "1.1.0"
|
||||
unicode-segmentation = "1.12.0"
|
||||
vello_cpu = { version = "0.0.7", default-features = false, features = ["std", "u8_pipeline", "f32_pipeline"] }
|
||||
vello_cpu = { version = "0.0.7", default-features = false, features = [
|
||||
"std",
|
||||
"u8_pipeline",
|
||||
"f32_pipeline",
|
||||
] }
|
||||
wasm-bindgen = "0.2.108" # Keep wasm-bindgen version in sync in: setup_web.sh, Cargo.toml, Cargo.lock, rust.yml. Don't update this spuriously, because of https://github.com/rerun-io/rerun/issues/8766
|
||||
wasm-bindgen-futures = "0.4.58"
|
||||
wayland-cursor = { version = "0.31.11", default-features = false }
|
||||
|
||||
@@ -722,6 +722,7 @@ impl WgpuWinitRunning<'_> {
|
||||
&clipped_primitives,
|
||||
&textures_delta,
|
||||
screenshot_commands,
|
||||
window,
|
||||
);
|
||||
|
||||
for action in viewport.actions_requested.drain(..) {
|
||||
@@ -1111,6 +1112,7 @@ fn render_immediate_viewport(
|
||||
&clipped_primitives,
|
||||
&textures_delta,
|
||||
vec![],
|
||||
window,
|
||||
);
|
||||
|
||||
egui_winit.handle_platform_output(window, platform_output);
|
||||
|
||||
@@ -56,8 +56,13 @@ impl TextAgent {
|
||||
let input = input.clone();
|
||||
move |event: web_sys::InputEvent, runner: &mut AppRunner| {
|
||||
let text = input.value();
|
||||
// Fix android virtual keyboard Gboard
|
||||
// This removes the virtual keyboard's suggestion.
|
||||
// Workaround for an Android Gboard issue: after typing a word,
|
||||
// the user has to delete invisible characters (whose count
|
||||
// matches the length of the current suggestion) before actual
|
||||
// characters are deleted, unless the focus has been reset.
|
||||
//
|
||||
// this issue appears to have been fixed in Gboard sometime
|
||||
// between versions 14.7.09 and 17.0.12.
|
||||
if !event.is_composing() {
|
||||
input.blur().ok();
|
||||
input.focus().ok();
|
||||
@@ -164,6 +169,12 @@ impl TextAgent {
|
||||
|
||||
let Some(ime) = ime else { return Ok(()) };
|
||||
|
||||
if ime.should_interrupt_composition {
|
||||
// no-op for now: currently, the text agent is sizeless, so any
|
||||
// click shifts focus to the canvas, which naturally interrupts the
|
||||
// composition.
|
||||
}
|
||||
|
||||
let mut canvas_rect = super::canvas_content_rect(canvas);
|
||||
// Fix for safari with virtual keyboard flapping position
|
||||
if is_mobile_safari() {
|
||||
|
||||
@@ -411,6 +411,7 @@ impl Painter {
|
||||
/// and the captures captured screenshot if it was requested.
|
||||
///
|
||||
/// If `capture_data` isn't empty, a screenshot will be captured.
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn paint_and_update_textures(
|
||||
&mut self,
|
||||
viewport_id: ViewportId,
|
||||
@@ -419,6 +420,7 @@ impl Painter {
|
||||
clipped_primitives: &[epaint::ClippedPrimitive],
|
||||
textures_delta: &epaint::textures::TexturesDelta,
|
||||
capture_data: Vec<UserData>,
|
||||
window: &winit::window::Window,
|
||||
) -> f32 {
|
||||
profiling::function_scope!();
|
||||
|
||||
@@ -654,6 +656,8 @@ impl Painter {
|
||||
);
|
||||
}
|
||||
|
||||
window.pre_present_notify();
|
||||
|
||||
{
|
||||
profiling::scope!("present");
|
||||
// wgpu doesn't document where vsync can happen. Maybe here?
|
||||
|
||||
@@ -1057,7 +1057,8 @@ impl State {
|
||||
self.set_cursor_icon(window, cursor_icon);
|
||||
|
||||
let allow_ime = ime.is_some();
|
||||
if self.allow_ime != allow_ime {
|
||||
let is_toggling_ime = self.allow_ime != allow_ime;
|
||||
if is_toggling_ime {
|
||||
self.allow_ime = allow_ime;
|
||||
#[cfg(target_os = "windows")]
|
||||
if !self.allow_ime {
|
||||
@@ -1074,6 +1075,14 @@ impl State {
|
||||
}
|
||||
|
||||
if let Some(ime) = ime {
|
||||
if !is_toggling_ime && ime.should_interrupt_composition {
|
||||
// TODO(umajho): use a more proper way to interrupt composition
|
||||
// if `winit` provides one in the future.
|
||||
|
||||
window.set_ime_allowed(false);
|
||||
window.set_ime_allowed(true);
|
||||
}
|
||||
|
||||
let pixels_per_point = pixels_per_point(&self.egui_ctx, window);
|
||||
let ime_rect_px = pixels_per_point * ime.rect;
|
||||
if self.ime_rect_px != Some(ime_rect_px)
|
||||
|
||||
@@ -793,7 +793,7 @@ impl Context {
|
||||
let plugins = self.read(|ctx| ctx.plugins.ordered_plugins());
|
||||
#[expect(deprecated)]
|
||||
self.run(new_input, |ctx| {
|
||||
let mut top_ui = Ui::new(
|
||||
let mut root_ui = Ui::new(
|
||||
ctx.clone(),
|
||||
Id::new((ctx.viewport_id(), "__top_ui")),
|
||||
UiBuilder::new()
|
||||
@@ -802,14 +802,15 @@ impl Context {
|
||||
);
|
||||
|
||||
{
|
||||
plugins.on_begin_pass(&mut top_ui);
|
||||
run_ui(&mut top_ui);
|
||||
plugins.on_end_pass(&mut top_ui);
|
||||
plugins.on_begin_pass(&mut root_ui);
|
||||
run_ui(&mut root_ui);
|
||||
plugins.on_end_pass(&mut root_ui);
|
||||
}
|
||||
|
||||
// Inform ctx about what we actually used, so we can shrink the native window to fit.
|
||||
// TODO(emilk): make better use of this somehow
|
||||
ctx.pass_state_mut(|state| state.allocate_central_panel(top_ui.min_rect()));
|
||||
ctx.pass_state_mut(|state| {
|
||||
state.root_ui_available_rect = Some(root_ui.available_rect_before_wrap());
|
||||
state.root_ui_min_rect = Some(root_ui.min_rect());
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2611,6 +2612,12 @@ impl ContextImpl {
|
||||
|
||||
let mut platform_output: PlatformOutput = std::mem::take(&mut viewport.output);
|
||||
|
||||
if self.memory.should_interrupt_ime()
|
||||
&& let Some(ime) = &mut platform_output.ime
|
||||
{
|
||||
ime.should_interrupt_composition = true;
|
||||
}
|
||||
|
||||
{
|
||||
profiling::scope!("accesskit");
|
||||
let state = viewport.this_pass.accesskit_state.take();
|
||||
@@ -2853,13 +2860,21 @@ impl Context {
|
||||
/// How much space is still available after panels have been added.
|
||||
#[deprecated = "Use content_rect (or viewport_rect) instead"]
|
||||
pub fn available_rect(&self) -> Rect {
|
||||
#[expect(deprecated)] // legacy
|
||||
self.pass_state(|s| s.available_rect()).round_ui()
|
||||
}
|
||||
|
||||
/// How much space is used by windows and the top-level [`Ui`].
|
||||
pub fn globally_used_rect(&self) -> Rect {
|
||||
self.write(|ctx| {
|
||||
let mut used = ctx.viewport().this_pass.used_by_panels;
|
||||
let viewport = ctx.viewport();
|
||||
let root_ui_min_rect =
|
||||
(viewport.this_pass.root_ui_min_rect).or(viewport.prev_pass.root_ui_min_rect);
|
||||
|
||||
let mut used = root_ui_min_rect.unwrap_or_else(|| {
|
||||
#[expect(deprecated)] // legacy
|
||||
ctx.viewport().this_pass.used_by_panels
|
||||
});
|
||||
for (_id, window) in ctx.memory.areas().visible_windows() {
|
||||
used |= window.rect();
|
||||
}
|
||||
@@ -2886,18 +2901,27 @@ impl Context {
|
||||
/// Is the pointer (mouse/touch) over any egui area?
|
||||
pub fn is_pointer_over_egui(&self) -> bool {
|
||||
let pointer_pos = self.input(|i| i.pointer.interact_pos());
|
||||
if let Some(pointer_pos) = pointer_pos {
|
||||
if let Some(layer) = self.layer_id_at(pointer_pos) {
|
||||
if layer.order == Order::Background {
|
||||
!self.pass_state(|state| state.unused_rect.contains(pointer_pos))
|
||||
} else {
|
||||
true
|
||||
}
|
||||
let Some(pointer_pos) = pointer_pos else {
|
||||
return false;
|
||||
};
|
||||
let Some(layer) = self.layer_id_at(pointer_pos) else {
|
||||
return false;
|
||||
};
|
||||
if layer.order == Order::Background {
|
||||
let root_ui_available_rect = self
|
||||
.pass_state(|state| state.root_ui_available_rect)
|
||||
.or_else(|| self.prev_pass_state(|state| state.root_ui_available_rect));
|
||||
|
||||
if let Some(root_ui_available_rect) = root_ui_available_rect {
|
||||
// Modern `run_ui` code
|
||||
!root_ui_available_rect.contains(pointer_pos)
|
||||
} else {
|
||||
false
|
||||
// Legacy code
|
||||
#[expect(deprecated)]
|
||||
!self.pass_state(|state| state.unused_rect.contains(pointer_pos))
|
||||
}
|
||||
} else {
|
||||
false
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,9 @@ pub struct IMEOutput {
|
||||
///
|
||||
/// This is a very thin rectangle.
|
||||
pub cursor_rect: crate::Rect,
|
||||
|
||||
/// Whether any ongoing IME composition should be interrupted.
|
||||
pub should_interrupt_composition: bool,
|
||||
}
|
||||
|
||||
/// Commands that the egui integration should execute at the end of a frame.
|
||||
|
||||
@@ -117,21 +117,10 @@ pub struct Memory {
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
popups: ViewportIdMap<OpenPopup>,
|
||||
|
||||
/// When the last IME interruption was made.
|
||||
/// Whether to inform the backend to interrupt any ongoing IME composition
|
||||
/// this pass.
|
||||
#[cfg_attr(feature = "persistence", serde(skip))]
|
||||
ime_interruption_time: ImeInterruptionTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
enum ImeInterruptionTime {
|
||||
#[default]
|
||||
None,
|
||||
|
||||
/// The IME was interrupted in the current frame.
|
||||
ThisFrame,
|
||||
|
||||
/// The IME was interrupted in the previous frame.
|
||||
LastFrame,
|
||||
requested_interrupt_ime: bool,
|
||||
}
|
||||
|
||||
impl Default for Memory {
|
||||
@@ -149,7 +138,7 @@ impl Default for Memory {
|
||||
popups: Default::default(),
|
||||
everything_is_visible: Default::default(),
|
||||
add_fonts: Default::default(),
|
||||
ime_interruption_time: Default::default(),
|
||||
requested_interrupt_ime: Default::default(),
|
||||
};
|
||||
slf.interactions.entry(slf.viewport_id).or_default();
|
||||
slf.areas.entry(slf.viewport_id).or_default();
|
||||
@@ -778,15 +767,7 @@ impl Memory {
|
||||
|
||||
self.areas.entry(self.viewport_id).or_default();
|
||||
|
||||
match self.ime_interruption_time {
|
||||
ImeInterruptionTime::ThisFrame => {
|
||||
self.ime_interruption_time = ImeInterruptionTime::LastFrame;
|
||||
}
|
||||
ImeInterruptionTime::LastFrame => {
|
||||
self.ime_interruption_time = ImeInterruptionTime::None;
|
||||
}
|
||||
ImeInterruptionTime::None => {}
|
||||
}
|
||||
self.requested_interrupt_ime = false;
|
||||
|
||||
// self.interactions is handled elsewhere
|
||||
|
||||
@@ -1028,30 +1009,22 @@ impl Memory {
|
||||
///
|
||||
/// A widget should only consume IME events if this returns `true`. At most
|
||||
/// one widget can own IME events for each frame.
|
||||
#[inline(always)]
|
||||
pub fn owns_ime_events(&self, id: Id) -> bool {
|
||||
let Some(focus) = self.focus() else {
|
||||
return false;
|
||||
};
|
||||
// We check across two frames because the widget that called
|
||||
// `interrupt_ime` may run after other widgets that call this method
|
||||
// within the same frame.
|
||||
if matches!(
|
||||
self.ime_interruption_time,
|
||||
ImeInterruptionTime::ThisFrame | ImeInterruptionTime::LastFrame
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
focus.focused() == Some(id)
|
||||
// Note: Even if the IME is being interrupted in the current frame, we
|
||||
// should not return `false` here, since we still need
|
||||
// `PlatformOutput::ime` to be set in such cases.
|
||||
|
||||
self.has_focus(id)
|
||||
}
|
||||
|
||||
/// Interrupt the current IME composition, if any.
|
||||
///
|
||||
/// This causes [`Self::owns_ime_events`] to return `false` for all widgets
|
||||
/// for the remainder of this frame and the next frame, giving time
|
||||
/// for the IME to be dismissed (by making `platform_output.ime` be `None`
|
||||
/// for at least one frame).
|
||||
pub fn interrupt_ime(&mut self) {
|
||||
self.ime_interruption_time = ImeInterruptionTime::ThisFrame;
|
||||
self.requested_interrupt_ime = true;
|
||||
}
|
||||
|
||||
pub(crate) fn should_interrupt_ime(&self) -> bool {
|
||||
self.requested_interrupt_ime
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![expect(deprecated)] // TODO(emilk): Remove legacy panels
|
||||
|
||||
use ahash::HashMap;
|
||||
|
||||
use crate::{Align, Id, IdMap, LayerId, Rangef, Rect, Vec2, WidgetRects, id::IdSet, style};
|
||||
@@ -199,15 +201,28 @@ pub struct PassState {
|
||||
|
||||
pub tooltips: TooltipPassState,
|
||||
|
||||
/// What the root UI had available at the end of the previous pass.
|
||||
///
|
||||
/// Only set if [`crate::Context::run_ui`] has been called.
|
||||
pub root_ui_available_rect: Option<Rect>,
|
||||
|
||||
/// What the root UI had used at the end of the previous pass.
|
||||
///
|
||||
/// Only set if [`crate::Context::run_ui`] has been called.
|
||||
pub root_ui_min_rect: Option<Rect>,
|
||||
|
||||
/// Starts off as the `screen_rect`, shrinks as panels are added.
|
||||
/// The [`crate::CentralPanel`] does not change this.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub available_rect: Rect,
|
||||
|
||||
/// Starts off as the `screen_rect`, shrinks as panels are added.
|
||||
/// The [`crate::CentralPanel`] retracts from this.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub unused_rect: Rect,
|
||||
|
||||
/// How much space is used by panels.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub used_by_panels: Rect,
|
||||
|
||||
/// The current scroll area should scroll to this range (horizontal, vertical).
|
||||
@@ -240,6 +255,8 @@ impl Default for PassState {
|
||||
widgets: Default::default(),
|
||||
layers: Default::default(),
|
||||
tooltips: Default::default(),
|
||||
root_ui_available_rect: None,
|
||||
root_ui_min_rect: None,
|
||||
available_rect: Rect::NAN,
|
||||
unused_rect: Rect::NAN,
|
||||
used_by_panels: Rect::NAN,
|
||||
@@ -262,6 +279,8 @@ impl PassState {
|
||||
widgets,
|
||||
tooltips,
|
||||
layers,
|
||||
root_ui_available_rect,
|
||||
root_ui_min_rect,
|
||||
available_rect,
|
||||
unused_rect,
|
||||
used_by_panels,
|
||||
@@ -278,6 +297,8 @@ impl PassState {
|
||||
widgets.clear();
|
||||
tooltips.clear();
|
||||
layers.clear();
|
||||
*root_ui_available_rect = None;
|
||||
*root_ui_min_rect = None;
|
||||
*available_rect = content_rect;
|
||||
*unused_rect = content_rect;
|
||||
*used_by_panels = Rect::NOTHING;
|
||||
@@ -295,6 +316,7 @@ impl PassState {
|
||||
}
|
||||
|
||||
/// How much space is still available after panels has been added.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub(crate) fn available_rect(&self) -> Rect {
|
||||
debug_assert!(
|
||||
self.available_rect.is_finite(),
|
||||
@@ -304,6 +326,7 @@ impl PassState {
|
||||
}
|
||||
|
||||
/// Shrink `available_rect`.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub(crate) fn allocate_left_panel(&mut self, panel_rect: Rect) {
|
||||
debug_assert!(
|
||||
panel_rect.min.distance(self.available_rect.min) < 0.1,
|
||||
@@ -315,6 +338,7 @@ impl PassState {
|
||||
}
|
||||
|
||||
/// Shrink `available_rect`.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub(crate) fn allocate_right_panel(&mut self, panel_rect: Rect) {
|
||||
debug_assert!(
|
||||
panel_rect.max.distance(self.available_rect.max) < 0.1,
|
||||
@@ -326,6 +350,7 @@ impl PassState {
|
||||
}
|
||||
|
||||
/// Shrink `available_rect`.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub(crate) fn allocate_top_panel(&mut self, panel_rect: Rect) {
|
||||
debug_assert!(
|
||||
panel_rect.min.distance(self.available_rect.min) < 0.1,
|
||||
@@ -337,6 +362,7 @@ impl PassState {
|
||||
}
|
||||
|
||||
/// Shrink `available_rect`.
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub(crate) fn allocate_bottom_panel(&mut self, panel_rect: Rect) {
|
||||
debug_assert!(
|
||||
panel_rect.max.distance(self.available_rect.max) < 0.1,
|
||||
@@ -347,6 +373,7 @@ impl PassState {
|
||||
self.used_by_panels |= panel_rect;
|
||||
}
|
||||
|
||||
#[deprecated = "Only used by legacy Context-Panels"]
|
||||
pub(crate) fn allocate_central_panel(&mut self, panel_rect: Rect) {
|
||||
// Note: we do not shrink `available_rect`, because
|
||||
// we allow windows to cover the CentralPanel.
|
||||
|
||||
@@ -894,6 +894,7 @@ impl TextEdit<'_> {
|
||||
o.ime = Some(crate::output::IMEOutput {
|
||||
rect: to_global * inner_rect,
|
||||
cursor_rect: to_global * primary_cursor_rect,
|
||||
should_interrupt_composition: false,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,7 +11,13 @@ readme = "README.md"
|
||||
repository = "https://github.com/emilk/egui/tree/main/crates/egui_glow"
|
||||
categories = ["gui", "game-development"]
|
||||
keywords = ["glow", "egui", "gui", "gamedev"]
|
||||
include = ["../../LICENSE-APACHE", "../../LICENSE-MIT", "**/*.rs", "Cargo.toml", "src/shader/*.glsl"]
|
||||
include = [
|
||||
"../../LICENSE-APACHE",
|
||||
"../../LICENSE-MIT",
|
||||
"**/*.rs",
|
||||
"Cargo.toml",
|
||||
"src/shader/*.glsl",
|
||||
]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -65,7 +71,6 @@ winit = { workspace = true, optional = true, default-features = false, features
|
||||
web-sys = { workspace = true, features = ["console"] }
|
||||
wasm-bindgen.workspace = true
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
glutin = { workspace = true, default-features = true } # examples/pure_glow
|
||||
glutin-winit = { workspace = true, default-features = true }
|
||||
|
||||
@@ -38,7 +38,7 @@ egui.workspace = true
|
||||
eframe = { workspace = true, optional = true }
|
||||
kittest.workspace = true
|
||||
serde.workspace = true
|
||||
toml = {workspace = true, features = ["parse", "serde"] }
|
||||
toml = { workspace = true, features = ["parse", "serde"] }
|
||||
|
||||
# wgpu dependencies
|
||||
egui-wgpu = { workspace = true, optional = true }
|
||||
|
||||
@@ -48,7 +48,14 @@ mint = ["emath/mint"]
|
||||
rayon = ["dep:rayon"]
|
||||
|
||||
## Allow serialization using [`serde`](https://docs.rs/serde).
|
||||
serde = ["dep:serde", "ahash/serde", "emath/serde", "ecolor/serde", "font-types/serde", "smallvec/serde"]
|
||||
serde = [
|
||||
"dep:serde",
|
||||
"ahash/serde",
|
||||
"ecolor/serde",
|
||||
"emath/serde",
|
||||
"font-types/serde",
|
||||
"smallvec/serde",
|
||||
]
|
||||
|
||||
## Change Vertex layout to be compatible with unity
|
||||
unity = []
|
||||
|
||||
@@ -13,7 +13,8 @@ workspace = true
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
eframe = { workspace = true, features = [
|
||||
eframe = { workspace = true, default_features = false, features = [
|
||||
"glow",
|
||||
"default",
|
||||
"__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO
|
||||
] }
|
||||
|
||||
@@ -30,6 +30,10 @@ struct MyTestApp {}
|
||||
|
||||
impl eframe::App for MyTestApp {
|
||||
fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) {
|
||||
egui::Panel::top("top").show_inside(ui, |ui| {
|
||||
ui.label("This is a test of painting directly with glow.");
|
||||
});
|
||||
|
||||
use glow::HasContext as _;
|
||||
let gl = frame.gl().unwrap();
|
||||
|
||||
@@ -43,6 +47,21 @@ impl eframe::App for MyTestApp {
|
||||
|
||||
egui::Window::new("Floating Window").show(ui.ctx(), |ui| {
|
||||
ui.label("The background should be purple.");
|
||||
ui.label(format!(
|
||||
"is_pointer_over_egui: {}",
|
||||
ui.is_pointer_over_egui()
|
||||
));
|
||||
ui.label(format!(
|
||||
"egui_wants_pointer_input: {}",
|
||||
ui.egui_wants_pointer_input()
|
||||
));
|
||||
ui.label(format!(
|
||||
"egui_is_using_pointer: {}",
|
||||
ui.egui_is_using_pointer()
|
||||
));
|
||||
if let Some(pos) = ui.pointer_latest_pos() {
|
||||
ui.label(format!("layer_id_at: {:?}", ui.layer_id_at(pos)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user