mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 04:40:03 -04:00
Merge branch 'main' into lucas/text-edit-min-size
This commit is contained in:
@@ -41,7 +41,6 @@ impl Hsva {
|
||||
/// From linear RGBA with premultiplied alpha
|
||||
#[inline]
|
||||
pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
#![expect(clippy::many_single_char_names)]
|
||||
if a <= 0.0 {
|
||||
if r == 0.0 && b == 0.0 && a == 0.0 {
|
||||
Self::default()
|
||||
@@ -57,7 +56,6 @@ impl Hsva {
|
||||
/// From linear RGBA without premultiplied alpha
|
||||
#[inline]
|
||||
pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self {
|
||||
#![expect(clippy::many_single_char_names)]
|
||||
let (h, s, v) = hsv_from_rgb([r, g, b]);
|
||||
Self { h, s, v, a }
|
||||
}
|
||||
@@ -189,7 +187,6 @@ impl From<Color32> for Hsva {
|
||||
/// All ranges in 0-1, rgb is linear.
|
||||
#[inline]
|
||||
pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) {
|
||||
#![expect(clippy::many_single_char_names)]
|
||||
let min = r.min(g.min(b));
|
||||
let max = r.max(g.max(b)); // value
|
||||
|
||||
@@ -213,7 +210,6 @@ pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) {
|
||||
/// All ranges in 0-1, rgb is linear.
|
||||
#[inline]
|
||||
pub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] {
|
||||
#![expect(clippy::many_single_char_names)]
|
||||
let h = (h.fract() + 1.0).fract(); // wrap
|
||||
let s = s.clamp(0.0, 1.0);
|
||||
|
||||
|
||||
@@ -161,16 +161,16 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus {
|
||||
if icon_big.is_null() {
|
||||
log::warn!("Failed to create HICON (for big icon) from embedded png data.");
|
||||
return AppIconStatus::NotSetIgnored; // We could try independently with the small icon but what's the point, it would look bad!
|
||||
} else {
|
||||
// SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior.
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
window_handle,
|
||||
WM_SETICON,
|
||||
ICON_BIG as usize,
|
||||
icon_big as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior.
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
window_handle,
|
||||
WM_SETICON,
|
||||
ICON_BIG as usize,
|
||||
icon_big as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
{
|
||||
@@ -180,16 +180,16 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus {
|
||||
if icon_small.is_null() {
|
||||
log::warn!("Failed to create HICON (for small icon) from embedded png data.");
|
||||
return AppIconStatus::NotSetIgnored;
|
||||
} else {
|
||||
// SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior.
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
window_handle,
|
||||
WM_SETICON,
|
||||
ICON_SMALL as usize,
|
||||
icon_small as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior.
|
||||
unsafe {
|
||||
SendMessageW(
|
||||
window_handle,
|
||||
WM_SETICON,
|
||||
ICON_SMALL as usize,
|
||||
icon_small as isize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ fn roaming_appdata() -> Option<PathBuf> {
|
||||
&FOLDERID_RoamingAppData,
|
||||
KF_FLAG_DONT_VERIFY as u32,
|
||||
core::ptr::null_mut(),
|
||||
&mut path_raw,
|
||||
&raw mut path_raw,
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
@@ -1720,7 +1720,7 @@ fn save_screenshot_and_exit(
|
||||
screen_size_in_pixels: [u32; 2],
|
||||
) {
|
||||
assert!(
|
||||
path.ends_with(".png"),
|
||||
egui::load::has_extension(path, "png"),
|
||||
"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}"
|
||||
);
|
||||
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
||||
|
||||
@@ -1761,11 +1761,11 @@ impl Context {
|
||||
.get(&id)
|
||||
.map(|v| v.repaint.cumulative_frame_nr)
|
||||
.unwrap_or_else(|| {
|
||||
if cfg!(debug_assertions) {
|
||||
panic!("cumulative_frame_nr_for failed to find the viewport {id:?}");
|
||||
} else {
|
||||
0
|
||||
}
|
||||
debug_assert!(
|
||||
false,
|
||||
"cumulative_frame_nr_for failed to find the viewport {id:?}"
|
||||
);
|
||||
0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,6 +41,13 @@ impl<T: core::hash::Hash + core::fmt::Debug> AsId for T {}
|
||||
/// This is niche-optimized to that `Option<Id>` is the same size as `Id`.
|
||||
#[derive(Clone, Copy, Hash, Eq, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
expect(
|
||||
clippy::unsafe_derive_deserialize,
|
||||
reason = "`from_high_entropy_bits` is only `unsafe` about entropy, not memory safety"
|
||||
)
|
||||
)]
|
||||
pub struct Id(NonZeroU64);
|
||||
|
||||
impl nohash_hasher::IsEnabled for Id {}
|
||||
|
||||
@@ -306,6 +306,19 @@ macro_rules! generate_loader_id {
|
||||
}
|
||||
pub use crate::generate_loader_id;
|
||||
|
||||
/// Does the given URI end with the given file extension?
|
||||
///
|
||||
/// The comparison ignores ASCII case and any `#fragment` at the end of the URI,
|
||||
/// so `has_extension("cat.GIF#frame=2", "gif")` is `true`.
|
||||
///
|
||||
/// This is useful when implementing an [`ImageLoader`].
|
||||
pub fn has_extension(uri: &str, extension: &str) -> bool {
|
||||
let path = uri.split('#').next().unwrap_or(uri);
|
||||
std::path::Path::new(path)
|
||||
.extension()
|
||||
.is_some_and(|found| found.eq_ignore_ascii_case(extension))
|
||||
}
|
||||
|
||||
pub type BytesLoadResult = Result<BytesPoll>;
|
||||
|
||||
/// Represents a loader capable of loading raw unstructured bytes from somewhere,
|
||||
@@ -639,3 +652,14 @@ impl Loaders {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_extension() {
|
||||
assert!(has_extension("cat.svg", "svg"));
|
||||
assert!(has_extension("cat.SVG", "svg"));
|
||||
assert!(has_extension("http://example.com/cat.gif#frame=2", "gif"));
|
||||
assert!(!has_extension("cat.svg.png", "svg"));
|
||||
assert!(!has_extension("svg", "svg"));
|
||||
assert!(!has_extension("cat.jpeg", "jpg"));
|
||||
assert!(!has_extension("cat.svg?v=1", "svg"));
|
||||
}
|
||||
|
||||
@@ -150,5 +150,5 @@ impl TextureLoader for DefaultTextureLoader {
|
||||
}
|
||||
|
||||
fn is_svg(uri: &str) -> bool {
|
||||
uri.ends_with(".svg")
|
||||
super::has_extension(uri, "svg")
|
||||
}
|
||||
|
||||
@@ -934,7 +934,7 @@ fn animated_image_frame_index(ctx: &Context, uri: &str) -> usize {
|
||||
|
||||
/// Checks if uri is a gif file
|
||||
fn is_gif_uri(uri: &str) -> bool {
|
||||
uri.ends_with(".gif") || uri.contains(".gif#")
|
||||
crate::load::has_extension(uri, "gif")
|
||||
}
|
||||
|
||||
/// Checks if bytes are gifs
|
||||
@@ -944,7 +944,7 @@ pub fn has_gif_magic_header(bytes: &[u8]) -> bool {
|
||||
|
||||
/// Checks if uri is a webp file
|
||||
fn is_webp_uri(uri: &str) -> bool {
|
||||
uri.ends_with(".webp") || uri.contains(".webp#")
|
||||
crate::load::has_extension(uri, "webp")
|
||||
}
|
||||
|
||||
/// Checks if bytes are webp
|
||||
|
||||
@@ -118,15 +118,15 @@ impl<'a> Parser<'a> {
|
||||
{
|
||||
let language = &language_start[..newline];
|
||||
let code_start = &language_start[newline + 1..];
|
||||
if let Some(end) = code_start.find("\n```") {
|
||||
return if let Some(end) = code_start.find("\n```") {
|
||||
let code = &code_start[..end].trim();
|
||||
self.s = &code_start[end + 4..];
|
||||
self.start_of_line = false;
|
||||
return Some(Item::CodeBlock(language, code));
|
||||
Some(Item::CodeBlock(language, code))
|
||||
} else {
|
||||
self.s = "";
|
||||
return Some(Item::CodeBlock(language, code_start));
|
||||
}
|
||||
Some(Item::CodeBlock(language, code_start))
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -138,18 +138,18 @@ impl<'a> Parser<'a> {
|
||||
self.start_of_line = false;
|
||||
self.style.code = true;
|
||||
let rest_of_line = &self.s[..self.s.find('\n').unwrap_or(self.s.len())];
|
||||
if let Some(end) = rest_of_line.find('`') {
|
||||
return if let Some(end) = rest_of_line.find('`') {
|
||||
let item = Item::Text(self.style, &self.s[..end]);
|
||||
self.s = &self.s[end + 1..];
|
||||
self.style.code = false;
|
||||
return Some(item);
|
||||
Some(item)
|
||||
} else {
|
||||
let end = rest_of_line.len();
|
||||
let item = Item::Text(self.style, rest_of_line);
|
||||
self.s = &self.s[end..];
|
||||
self.style.code = false;
|
||||
return Some(item);
|
||||
}
|
||||
Some(item)
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ impl SvgLoader {
|
||||
}
|
||||
|
||||
fn is_supported(uri: &str) -> bool {
|
||||
uri.ends_with(".svg")
|
||||
egui::load::has_extension(uri, "svg")
|
||||
}
|
||||
|
||||
impl Default for SvgLoader {
|
||||
|
||||
@@ -210,6 +210,13 @@ impl SyntectTheme {
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(default)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
all(feature = "serde", not(feature = "syntect")),
|
||||
expect(
|
||||
clippy::unsafe_derive_deserialize,
|
||||
reason = "the `enum_map!` macro expands to `unsafe` code"
|
||||
)
|
||||
)]
|
||||
pub struct CodeTheme {
|
||||
dark_mode: bool,
|
||||
|
||||
|
||||
@@ -247,16 +247,16 @@ impl<'a, State> Harness<'a, State> {
|
||||
pub fn step(&mut self) {
|
||||
let events = core::mem::take(&mut *self.queued_events.lock());
|
||||
if events.is_empty() {
|
||||
self._step(false);
|
||||
self.step_impl(false);
|
||||
}
|
||||
for event in events {
|
||||
self.input.events.push(event);
|
||||
self._step(false);
|
||||
self.step_impl(false);
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a single step. This will not process any events.
|
||||
fn _step(&mut self, sizing_pass: bool) {
|
||||
fn step_impl(&mut self, sizing_pass: bool) {
|
||||
self.input.predicted_dt = self.step_dt;
|
||||
|
||||
let mut output = self.ctx.run_ui(self.input.take(), |ui| {
|
||||
@@ -297,7 +297,7 @@ impl<'a, State> Harness<'a, State> {
|
||||
/// [`Harness::new_ui`] / [`Harness::new_ui_state`] or
|
||||
/// [`HarnessBuilder::build_ui`] / [`HarnessBuilder::build_ui_state`].
|
||||
pub fn fit_contents(&mut self) {
|
||||
self._step(true);
|
||||
self.step_impl(true);
|
||||
|
||||
// Calculate size including all content (main UI + popups + tooltips)
|
||||
if let Some(rect) = self.compute_total_rect_with_popups() {
|
||||
@@ -333,7 +333,7 @@ impl<'a, State> Harness<'a, State> {
|
||||
}
|
||||
}
|
||||
|
||||
fn _try_run(&mut self, sleep: bool) -> Result<u64, ExceededMaxStepsError> {
|
||||
fn try_run_impl(&mut self, sleep: bool) -> Result<u64, ExceededMaxStepsError> {
|
||||
let mut steps = 0;
|
||||
loop {
|
||||
steps += 1;
|
||||
@@ -374,7 +374,7 @@ impl<'a, State> Harness<'a, State> {
|
||||
/// - [`Harness::run_steps`].
|
||||
/// - [`Harness::try_run_realtime`].
|
||||
pub fn try_run(&mut self) -> Result<u64, ExceededMaxStepsError> {
|
||||
self._try_run(false)
|
||||
self.try_run_impl(false)
|
||||
}
|
||||
|
||||
/// Run until
|
||||
@@ -414,7 +414,7 @@ impl<'a, State> Harness<'a, State> {
|
||||
/// - [`Harness::run_steps`].
|
||||
/// - [`Harness::try_run`].
|
||||
pub fn try_run_realtime(&mut self) -> Result<u64, ExceededMaxStepsError> {
|
||||
self._try_run(true)
|
||||
self.try_run_impl(true)
|
||||
}
|
||||
|
||||
/// Run a number of steps.
|
||||
|
||||
@@ -535,31 +535,31 @@ fn try_image_snapshot_options_impl(
|
||||
Ok(image) => image.to_rgba8(),
|
||||
Err(err) => {
|
||||
// No previous snapshot - probably a new test.
|
||||
if mode.is_update() {
|
||||
return update_snapshot();
|
||||
return if mode.is_update() {
|
||||
update_snapshot()
|
||||
} else {
|
||||
write_new_png()?;
|
||||
|
||||
return Err(SnapshotError::OpenSnapshot {
|
||||
Err(SnapshotError::OpenSnapshot {
|
||||
path: snapshot_path.clone(),
|
||||
err,
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
if previous.dimensions() != new.dimensions() {
|
||||
if mode.is_update() {
|
||||
return update_snapshot();
|
||||
return if mode.is_update() {
|
||||
update_snapshot()
|
||||
} else {
|
||||
write_new_png()?;
|
||||
|
||||
return Err(SnapshotError::SizeMismatch {
|
||||
Err(SnapshotError::SizeMismatch {
|
||||
name,
|
||||
expected: previous.dimensions(),
|
||||
actual: new.dimensions(),
|
||||
});
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
// Compare existing image to the new one:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#![expect(clippy::many_single_char_names)]
|
||||
|
||||
use core::ops::Range;
|
||||
|
||||
use crate::{Color32, PathShape, PathStroke, Shape};
|
||||
|
||||
@@ -1519,11 +1519,11 @@ impl Tessellator {
|
||||
|
||||
if stroke.is_empty() {
|
||||
return; // we are done
|
||||
} else {
|
||||
// we still need to do the stroke
|
||||
fill = Color32::TRANSPARENT; // don't fill again below
|
||||
break;
|
||||
}
|
||||
|
||||
// we still need to do the stroke
|
||||
fill = Color32::TRANSPARENT; // don't fill again below
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user