Refactored view-api to use Txt.
This commit is contained in:
parent
87c310c3b7
commit
2ab4d80d79
|
@ -138,3 +138,4 @@ bytemuck
|
|||
accesskit
|
||||
ime
|
||||
Imm
|
||||
ro
|
||||
|
|
|
@ -35,7 +35,6 @@ TextInput! {
|
|||
# Split Core
|
||||
|
||||
* Txt type crate.
|
||||
- Use it in the zero-ui-api crate.
|
||||
- Move `NameIdMap` to unique_id crate and make it generate for every ID type?
|
||||
|
||||
* Font service, segmenting and shaping.
|
||||
|
|
|
@ -671,7 +671,7 @@ impl TextEditor {
|
|||
self.txt_touched.set(false);
|
||||
}
|
||||
Err(e) => {
|
||||
self.handle_error("reading file", e.to_string()).await;
|
||||
self.handle_error("reading file", e.to_text()).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -718,7 +718,7 @@ impl TextEditor {
|
|||
}
|
||||
FileDialogResponse::Cancel => {}
|
||||
FileDialogResponse::Error(e) => {
|
||||
self.handle_error("saving file", e.to_string()).await;
|
||||
self.handle_error("saving file", e.to_text()).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -741,7 +741,7 @@ impl TextEditor {
|
|||
match r {
|
||||
Ok(()) => true,
|
||||
Err(e) => {
|
||||
self.handle_error("writing file", e.to_string()).await;
|
||||
self.handle_error("writing file", e.to_text()).await;
|
||||
false
|
||||
}
|
||||
}
|
||||
|
@ -768,14 +768,14 @@ impl TextEditor {
|
|||
}
|
||||
}
|
||||
|
||||
async fn handle_error(&self, context: &'static str, e: String) {
|
||||
async fn handle_error(&self, context: &'static str, e: Txt) {
|
||||
tracing::error!("error {context}, {e}");
|
||||
|
||||
use zero_ui::core::app::view_process::*;
|
||||
|
||||
let dlg = MsgDialog {
|
||||
title: "Error".into(),
|
||||
message: format!("Error {context}.\n\n{e}"),
|
||||
message: formatx!("Error {context}.\n\n{e}"),
|
||||
icon: MsgDialogIcon::Error,
|
||||
buttons: MsgDialogButtons::Ok,
|
||||
};
|
||||
|
|
|
@ -514,8 +514,8 @@ fn native() -> impl UiNode {
|
|||
on_click = async_hn!(|_| {
|
||||
use zero_ui::core::app::view_process::*;
|
||||
let rsp = WINDOWS.native_message_dialog(WINDOW.id(), MsgDialog {
|
||||
title: "Question?".to_owned(),
|
||||
message: "Example message. Yes -> Warn, No -> Error.".to_owned(),
|
||||
title: Txt::from_static("Question?"),
|
||||
message: Txt::from_static("Example message. Yes -> Warn, No -> Error."),
|
||||
icon: MsgDialogIcon::Info,
|
||||
buttons: MsgDialogButtons::YesNo,
|
||||
}).wait_rsp().await;
|
||||
|
@ -532,8 +532,8 @@ fn native() -> impl UiNode {
|
|||
},
|
||||
};
|
||||
WINDOWS.native_message_dialog(WINDOW.id(), MsgDialog {
|
||||
title: "Title".to_owned(),
|
||||
message: "Message".to_owned(),
|
||||
title: Txt::from_static("Title"),
|
||||
message: Txt::from_static("Message"),
|
||||
icon,
|
||||
buttons: MsgDialogButtons::Ok,
|
||||
});
|
||||
|
@ -594,7 +594,7 @@ fn native() -> impl UiNode {
|
|||
dlg.title = "Save File".into();
|
||||
dlg.kind = FileDialogKind::SaveFile;
|
||||
dlg.starting_dir = first_file.parent().map(|p| p.to_owned()).unwrap_or_default();
|
||||
dlg.starting_name = first_file.file_name().map(|p| p.to_string_lossy().into_owned()).unwrap_or_default();
|
||||
dlg.starting_name = first_file.file_name().map(|p| Txt::from_str(&p.to_string_lossy())).unwrap_or_default();
|
||||
let res = WINDOWS.native_file_dialog(WINDOW.id(), dlg.clone()).wait_rsp().await;
|
||||
let save_file = match res {
|
||||
FileDialogResponse::Selected(mut s) => {
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
sync::{self, Arc},
|
||||
};
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
pub use zero_ui_view_api::{
|
||||
self,
|
||||
api_extension::{ApiExtensionId, ApiExtensionName, ApiExtensionNameError, ApiExtensionPayload, ApiExtensionRecvError, ApiExtensions},
|
||||
|
@ -213,14 +214,14 @@ impl VIEW_PROCESS {
|
|||
/// Returns a list of image decoders supported by the view-process backend.
|
||||
///
|
||||
/// Each string is the lower-case file extension.
|
||||
pub fn image_decoders(&self) -> Result<Vec<String>> {
|
||||
pub fn image_decoders(&self) -> Result<Vec<Txt>> {
|
||||
self.write().process.image_decoders()
|
||||
}
|
||||
|
||||
/// Returns a list of image encoders supported by the view-process backend.
|
||||
///
|
||||
/// Each string is the lower-case file extension.
|
||||
pub fn image_encoders(&self) -> Result<Vec<String>> {
|
||||
pub fn image_encoders(&self) -> Result<Vec<Txt>> {
|
||||
self.write().process.image_encoders()
|
||||
}
|
||||
|
||||
|
@ -424,7 +425,7 @@ impl VIEW_PROCESS {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn on_image_error(&self, id: ImageId, error: String) -> Option<ViewImage> {
|
||||
pub(super) fn on_image_error(&self, id: ImageId, error: Txt) -> Option<ViewImage> {
|
||||
if let Some(i) = self.loading_image_index(id) {
|
||||
let img = self.write().loading_images.swap_remove(i).upgrade().unwrap();
|
||||
{
|
||||
|
@ -470,13 +471,13 @@ impl VIEW_PROCESS {
|
|||
i.map(|i| ViewImage(app.frame_images.swap_remove(i).upgrade().unwrap()))
|
||||
}
|
||||
|
||||
pub(super) fn on_image_encoded(&self, id: ImageId, format: String, data: IpcBytes) {
|
||||
pub(super) fn on_image_encoded(&self, id: ImageId, format: Txt, data: IpcBytes) {
|
||||
self.on_image_encode_result(id, format, Ok(data));
|
||||
}
|
||||
pub(super) fn on_image_encode_error(&self, id: ImageId, format: String, error: String) {
|
||||
pub(super) fn on_image_encode_error(&self, id: ImageId, format: Txt, error: Txt) {
|
||||
self.on_image_encode_result(id, format, Err(EncodeError::Encode(error)));
|
||||
}
|
||||
fn on_image_encode_result(&self, id: ImageId, format: String, result: std::result::Result<IpcBytes, EncodeError>) {
|
||||
fn on_image_encode_result(&self, id: ImageId, format: Txt, result: std::result::Result<IpcBytes, EncodeError>) {
|
||||
let mut app = self.write();
|
||||
app.encoding_images.retain(move |r| {
|
||||
let done = r.image_id == id && r.format == format;
|
||||
|
@ -509,7 +510,7 @@ impl VIEW_PROCESS {
|
|||
let mut app = self.write();
|
||||
app.pending_frames = 0;
|
||||
for (_, r) in app.message_dialogs.drain(..) {
|
||||
r.respond(MsgDialogResponse::Error("respawn".to_owned()));
|
||||
r.respond(MsgDialogResponse::Error(Txt::from_static("respawn")));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,7 +649,7 @@ impl ViewWindow {
|
|||
}
|
||||
|
||||
/// Set the window title.
|
||||
pub fn set_title(&self, title: String) -> Result<()> {
|
||||
pub fn set_title(&self, title: Txt) -> Result<()> {
|
||||
self.0.call(|id, p| p.set_title(id, title))
|
||||
}
|
||||
|
||||
|
@ -1100,7 +1101,7 @@ struct ViewImageData {
|
|||
is_opaque: bool,
|
||||
|
||||
partial_pixels: Option<IpcBytes>,
|
||||
pixels: Option<std::result::Result<IpcBytes, String>>,
|
||||
pixels: Option<std::result::Result<IpcBytes, Txt>>,
|
||||
is_mask: bool,
|
||||
|
||||
done_signal: SignalOnce,
|
||||
|
@ -1151,7 +1152,7 @@ impl ViewImage {
|
|||
}
|
||||
|
||||
/// Returns the load error if one happened.
|
||||
pub fn error(&self) -> Option<String> {
|
||||
pub fn error(&self) -> Option<Txt> {
|
||||
self.0.read().pixels.as_ref().and_then(|s| s.as_ref().err().cloned())
|
||||
}
|
||||
|
||||
|
@ -1225,7 +1226,7 @@ impl ViewImage {
|
|||
}
|
||||
|
||||
/// Create a dummy image in the loaded or error state.
|
||||
pub fn dummy(error: Option<String>) -> Self {
|
||||
pub fn dummy(error: Option<Txt>) -> Self {
|
||||
ViewImage(Arc::new(RwLock::new(ViewImageData {
|
||||
app_id: None,
|
||||
id: None,
|
||||
|
@ -1255,7 +1256,7 @@ impl ViewImage {
|
|||
/// The `format` must be one of the [`image_encoders`] supported by the view-process backend.
|
||||
///
|
||||
/// [`image_encoders`]: View::image_encoders.
|
||||
pub async fn encode(&self, format: String) -> std::result::Result<IpcBytes, EncodeError> {
|
||||
pub async fn encode(&self, format: Txt) -> std::result::Result<IpcBytes, EncodeError> {
|
||||
self.awaiter().await;
|
||||
|
||||
if let Some(e) = self.error() {
|
||||
|
@ -1297,7 +1298,7 @@ impl ViewImage {
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum EncodeError {
|
||||
/// Encode error.
|
||||
Encode(String),
|
||||
Encode(Txt),
|
||||
/// Attempted to encode dummy image.
|
||||
///
|
||||
/// In a headless-app without renderer all images are dummy because there is no
|
||||
|
@ -1306,8 +1307,8 @@ pub enum EncodeError {
|
|||
/// The View-Process disconnected or has not finished initializing yet, try again after [`VIEW_PROCESS_INITED_EVENT`].
|
||||
ViewProcessOffline,
|
||||
}
|
||||
impl From<String> for EncodeError {
|
||||
fn from(e: String) -> Self {
|
||||
impl From<Txt> for EncodeError {
|
||||
fn from(e: Txt) -> Self {
|
||||
EncodeError::Encode(e)
|
||||
}
|
||||
}
|
||||
|
@ -1350,7 +1351,7 @@ impl WeakViewImage {
|
|||
|
||||
struct EncodeRequest {
|
||||
image_id: ImageId,
|
||||
format: String,
|
||||
format: Txt,
|
||||
listeners: Vec<flume::Sender<std::result::Result<IpcBytes, EncodeError>>>,
|
||||
}
|
||||
|
||||
|
@ -1360,16 +1361,16 @@ type ClipboardResult<T> = std::result::Result<T, ClipboardError>;
|
|||
pub struct ViewClipboard {}
|
||||
impl ViewClipboard {
|
||||
/// Read [`ClipboardType::Text`].
|
||||
pub fn read_text(&self) -> Result<ClipboardResult<String>> {
|
||||
pub fn read_text(&self) -> Result<ClipboardResult<Txt>> {
|
||||
match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::Text)? {
|
||||
Ok(ClipboardData::Text(t)) => Ok(Ok(t)),
|
||||
Err(e) => Ok(Err(e)),
|
||||
_ => Ok(Err(ClipboardError::Other("view-process returned incorrect type".to_owned()))),
|
||||
_ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write [`ClipboardType::Text`].
|
||||
pub fn write_text(&self, txt: String) -> Result<ClipboardResult<()>> {
|
||||
pub fn write_text(&self, txt: Txt) -> Result<ClipboardResult<()>> {
|
||||
VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Text(txt))
|
||||
}
|
||||
|
||||
|
@ -1379,7 +1380,7 @@ impl ViewClipboard {
|
|||
match app.process.read_clipboard(ClipboardType::Image)? {
|
||||
Ok(ClipboardData::Image(id)) => {
|
||||
if id == ImageId::INVALID {
|
||||
Ok(Err(ClipboardError::Other("view-process returned invalid image".to_owned())))
|
||||
Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned invalid image"))))
|
||||
} else {
|
||||
let img = ViewImage(Arc::new(RwLock::new(ViewImageData {
|
||||
id: Some(id),
|
||||
|
@ -1399,7 +1400,7 @@ impl ViewClipboard {
|
|||
}
|
||||
}
|
||||
Err(e) => Ok(Err(e)),
|
||||
_ => Ok(Err(ClipboardError::Other("view-process returned incorrect type".to_owned()))),
|
||||
_ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1410,7 +1411,7 @@ impl ViewClipboard {
|
|||
return VIEW_PROCESS.try_write()?.process.write_clipboard(ClipboardData::Image(id));
|
||||
}
|
||||
}
|
||||
Ok(Err(ClipboardError::Other("image not loaded".to_owned())))
|
||||
Ok(Err(ClipboardError::Other(Txt::from_static("image not loaded"))))
|
||||
}
|
||||
|
||||
/// Read [`ClipboardType::FileList`].
|
||||
|
@ -1418,7 +1419,7 @@ impl ViewClipboard {
|
|||
match VIEW_PROCESS.try_write()?.process.read_clipboard(ClipboardType::FileList)? {
|
||||
Ok(ClipboardData::FileList(f)) => Ok(Ok(f)),
|
||||
Err(e) => Ok(Err(e)),
|
||||
_ => Ok(Err(ClipboardError::Other("view-process returned incorrect type".to_owned()))),
|
||||
_ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1428,7 +1429,7 @@ impl ViewClipboard {
|
|||
}
|
||||
|
||||
/// Read [`ClipboardType::Extension`].
|
||||
pub fn read_extension(&self, data_type: String) -> Result<ClipboardResult<IpcBytes>> {
|
||||
pub fn read_extension(&self, data_type: Txt) -> Result<ClipboardResult<IpcBytes>> {
|
||||
match VIEW_PROCESS
|
||||
.try_write()?
|
||||
.process
|
||||
|
@ -1436,12 +1437,12 @@ impl ViewClipboard {
|
|||
{
|
||||
Ok(ClipboardData::Extension { data_type: rt, data }) if rt == data_type => Ok(Ok(data)),
|
||||
Err(e) => Ok(Err(e)),
|
||||
_ => Ok(Err(ClipboardError::Other("view-process returned incorrect type".to_owned()))),
|
||||
_ => Ok(Err(ClipboardError::Other(Txt::from_static("view-process returned incorrect type")))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write [`ClipboardType::Extension`].
|
||||
pub fn write_extension(&self, data_type: String, data: IpcBytes) -> Result<ClipboardResult<()>> {
|
||||
pub fn write_extension(&self, data_type: Txt, data: IpcBytes) -> Result<ClipboardResult<()>> {
|
||||
VIEW_PROCESS
|
||||
.try_write()?
|
||||
.process
|
||||
|
|
|
@ -54,7 +54,7 @@ pub enum ClipboardError {
|
|||
/// Other error.
|
||||
///
|
||||
/// The string can be a debug description of the error, only suitable for logging.
|
||||
Other(String),
|
||||
Other(Txt),
|
||||
}
|
||||
|
||||
/// Clipboard service.
|
||||
|
@ -99,7 +99,9 @@ impl CLIPBOARD {
|
|||
Ok(r) => match r {
|
||||
Ok(()) => Ok(()),
|
||||
Err(e) => match e {
|
||||
clipboard_api::ClipboardError::NotFound => Err(ClipboardError::Other("not found error in set operation".to_owned())),
|
||||
clipboard_api::ClipboardError::NotFound => {
|
||||
Err(ClipboardError::Other(Txt::from_static("not found error in set operation")))
|
||||
}
|
||||
clipboard_api::ClipboardError::NotSupported => Err(ClipboardError::NotSupported),
|
||||
clipboard_api::ClipboardError::Other(e) => Err(ClipboardError::Other(e)),
|
||||
},
|
||||
|
@ -118,7 +120,7 @@ impl CLIPBOARD {
|
|||
}
|
||||
/// Sets the text string on the clipboard, returns `Ok(())` if the operation succeeded.
|
||||
pub fn set_text(&self, txt: impl Into<Txt>) -> Result<(), ClipboardError> {
|
||||
self.set(|v| v.write_text(txt.into().into()))
|
||||
self.set(|v| v.write_text(txt.into()))
|
||||
}
|
||||
|
||||
/// Gets an image from the clipboard.
|
||||
|
@ -161,14 +163,14 @@ impl CLIPBOARD {
|
|||
/// Gets custom data from the clipboard.
|
||||
///
|
||||
/// The current view-process must support `data_type`.
|
||||
pub fn extension(&self, data_type: impl Into<String>) -> Result<Option<IpcBytes>, ClipboardError> {
|
||||
pub fn extension(&self, data_type: impl Into<Txt>) -> Result<Option<IpcBytes>, ClipboardError> {
|
||||
self.get(|v| v.read_extension(data_type.into()))
|
||||
}
|
||||
|
||||
/// Set a custom data on the clipboard.
|
||||
///
|
||||
/// The current view-process must support `data_type`.
|
||||
pub fn set_extension(&self, data_type: impl Into<String>, data: IpcBytes) -> Result<(), ClipboardError> {
|
||||
pub fn set_extension(&self, data_type: impl Into<Txt>, data: IpcBytes) -> Result<(), ClipboardError> {
|
||||
self.set(|v| v.write_extension(data_type.into(), data))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,13 +13,13 @@ pub use trace::*;
|
|||
|
||||
mod local;
|
||||
pub use local::*;
|
||||
use zero_ui_txt::formatx;
|
||||
|
||||
use crate::{
|
||||
app::{AppDisconnected, AppEventSender, LoopTimer},
|
||||
context_var,
|
||||
crate_util::{Handle, HandleOwner, IdSet, WeakHandle},
|
||||
event::{Event, EventArgs, EventHandle, EventHandles, EventUpdate, EVENTS, EVENTS_SV},
|
||||
formatx,
|
||||
handler::{AppHandler, AppHandlerArgs, AppWeakHandle},
|
||||
render::ReuseRange,
|
||||
text::Txt,
|
||||
|
@ -768,7 +768,7 @@ impl WIDGET {
|
|||
} else if let Some(id) = self.try_id() {
|
||||
formatx!("<no-window>//{id:?}")
|
||||
} else {
|
||||
Txt::from("<no-widget>")
|
||||
Txt::from_str("<no-widget>")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1026,7 +1026,7 @@ impl CommandNameExt for Command {
|
|||
if shortcut.is_empty() {
|
||||
name.clone()
|
||||
} else {
|
||||
crate::formatx!("{name} ({})", shortcut[0])
|
||||
zero_ui_txt::formatx!("{name} ({})", shortcut[0])
|
||||
}
|
||||
})
|
||||
.boxed()
|
||||
|
|
|
@ -12,6 +12,7 @@ use std::{
|
|||
};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use zero_ui_txt::{formatx, ToText};
|
||||
use zero_ui_view_api::ipc::IpcBytes;
|
||||
|
||||
use crate::{
|
||||
|
@ -464,7 +465,7 @@ impl ImagesService {
|
|||
ImageSource::Read(path) => {
|
||||
let path = crate::crate_util::absolute_path(&path, || env::current_dir().expect("could not access current dir"), true);
|
||||
if !limits.allow_path.allows(&path) {
|
||||
let error = format!("limits filter blocked `{}`", path.display());
|
||||
let error = formatx!("limits filter blocked `{}`", path.display());
|
||||
tracing::error!("{error}");
|
||||
return var(Img::dummy(Some(error))).read_only();
|
||||
}
|
||||
|
@ -473,7 +474,7 @@ impl ImagesService {
|
|||
#[cfg(http)]
|
||||
ImageSource::Download(uri, accepts) => {
|
||||
if !limits.allow_uri.allows(&uri) {
|
||||
let error = format!("limits filter blocked `{uri}`");
|
||||
let error = formatx!("limits filter blocked `{uri}`");
|
||||
tracing::error!("{error}");
|
||||
return var(Img::dummy(Some(error))).read_only();
|
||||
}
|
||||
|
@ -552,15 +553,15 @@ impl ImagesService {
|
|||
format: path
|
||||
.extension()
|
||||
.and_then(|e| e.to_str())
|
||||
.map(|s| ImageDataFormat::FileExtension(s.to_owned()))
|
||||
.map(|s| ImageDataFormat::FileExtension(Txt::from_str(s)))
|
||||
.unwrap_or(ImageDataFormat::Unknown),
|
||||
r: Err(String::new()),
|
||||
r: Err(Txt::from_static("")),
|
||||
};
|
||||
|
||||
let mut file = match fs::File::open(path).await {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
r.r = Err(e.to_string());
|
||||
r.r = Err(e.to_text());
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
@ -568,20 +569,20 @@ impl ImagesService {
|
|||
let len = match file.metadata().await {
|
||||
Ok(m) => m.len() as usize,
|
||||
Err(e) => {
|
||||
r.r = Err(e.to_string());
|
||||
r.r = Err(e.to_text());
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
if len > max_encoded_size.0 {
|
||||
r.r = Err(format!("file size `{}` exceeds the limit of `{max_encoded_size}`", len.bytes()));
|
||||
r.r = Err(formatx!("file size `{}` exceeds the limit of `{max_encoded_size}`", len.bytes()));
|
||||
return r;
|
||||
}
|
||||
|
||||
let mut data = Vec::with_capacity(len);
|
||||
r.r = match file.read_to_end(&mut data).await {
|
||||
Ok(_) => Ok(IpcBytes::from_vec(data)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(e.to_text()),
|
||||
};
|
||||
|
||||
r
|
||||
|
@ -600,7 +601,7 @@ impl ImagesService {
|
|||
task::run(async move {
|
||||
let mut r = ImageData {
|
||||
format: ImageDataFormat::Unknown,
|
||||
r: Err(String::new()),
|
||||
r: Err(Txt::from_static("")),
|
||||
};
|
||||
|
||||
let request = task::http::Request::get(uri)
|
||||
|
@ -615,21 +616,21 @@ impl ImagesService {
|
|||
if let Some(m) = rsp.headers().get(&task::http::header::CONTENT_TYPE).and_then(|v| v.to_str().ok()) {
|
||||
let m = m.to_lowercase();
|
||||
if m.starts_with("image/") {
|
||||
r.format = ImageDataFormat::MimeType(m);
|
||||
r.format = ImageDataFormat::MimeType(Txt::from_str(&m));
|
||||
}
|
||||
}
|
||||
|
||||
match rsp.bytes().await {
|
||||
Ok(d) => r.r = Ok(IpcBytes::from_vec(d)),
|
||||
Err(e) => {
|
||||
r.r = Err(format!("download error: {e}"));
|
||||
r.r = Err(formatx!("download error: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
let _ = rsp.consume().await;
|
||||
}
|
||||
Err(e) => {
|
||||
r.r = Err(format!("request error: {e}"));
|
||||
r.r = Err(formatx!("request error: {e}"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,7 +789,7 @@ impl IMAGES {
|
|||
}
|
||||
|
||||
/// Returns a dummy image that reports it is loaded or an error.
|
||||
pub fn dummy(&self, error: Option<String>) -> ImageVar {
|
||||
pub fn dummy(&self, error: Option<Txt>) -> ImageVar {
|
||||
var(Img::dummy(error)).read_only()
|
||||
}
|
||||
|
||||
|
@ -805,7 +806,7 @@ impl IMAGES {
|
|||
pub fn download(&self, uri: impl task::http::TryUri, accept: Option<Txt>) -> ImageVar {
|
||||
match uri.try_uri() {
|
||||
Ok(uri) => self.cache(ImageSource::Download(uri, accept)),
|
||||
Err(e) => self.dummy(Some(e.to_string())),
|
||||
Err(e) => self.dummy(Some(e.to_text())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -951,5 +952,5 @@ impl IMAGES {
|
|||
}
|
||||
struct ImageData {
|
||||
format: ImageDataFormat,
|
||||
r: std::result::Result<IpcBytes, String>,
|
||||
r: std::result::Result<IpcBytes, Txt>,
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ impl Img {
|
|||
}
|
||||
|
||||
/// Create a dummy image in the loaded or error state.
|
||||
pub fn dummy(error: Option<String>) -> Self {
|
||||
pub fn dummy(error: Option<Txt>) -> Self {
|
||||
Self::new(ViewImage::dummy(error))
|
||||
}
|
||||
|
||||
|
@ -266,10 +266,10 @@ impl Img {
|
|||
}
|
||||
|
||||
/// Encode the image to the format.
|
||||
pub async fn encode(&self, format: String) -> std::result::Result<zero_ui_view_api::ipc::IpcBytes, EncodeError> {
|
||||
pub async fn encode(&self, format: Txt) -> std::result::Result<zero_ui_view_api::ipc::IpcBytes, EncodeError> {
|
||||
self.done_signal.clone().await;
|
||||
if let Some(e) = self.error() {
|
||||
Err(EncodeError::Encode(e.into()))
|
||||
Err(EncodeError::Encode(e))
|
||||
} else {
|
||||
self.view.get().unwrap().encode(format).await
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ impl Img {
|
|||
pub async fn save(&self, path: impl Into<PathBuf>) -> io::Result<()> {
|
||||
let path = path.into();
|
||||
if let Some(ext) = path.extension().and_then(|s| s.to_str()) {
|
||||
self.save_impl(ext.to_owned(), path).await
|
||||
self.save_impl(Txt::from_str(ext), path).await
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
|
@ -293,11 +293,11 @@ impl Img {
|
|||
/// Encode and write the image to `path`.
|
||||
///
|
||||
/// The image is encoded to the `format`, the file extension can be anything.
|
||||
pub async fn save_with_format(&self, format: String, path: impl Into<PathBuf>) -> io::Result<()> {
|
||||
pub async fn save_with_format(&self, format: Txt, path: impl Into<PathBuf>) -> io::Result<()> {
|
||||
self.save_impl(format, path.into()).await
|
||||
}
|
||||
|
||||
async fn save_impl(&self, format: String, path: PathBuf) -> io::Result<()> {
|
||||
async fn save_impl(&self, format: Txt, path: PathBuf) -> io::Result<()> {
|
||||
let view = self.view.get().unwrap();
|
||||
let data = view
|
||||
.encode(format)
|
||||
|
|
|
@ -1207,27 +1207,6 @@ pub enum UnderlinePosition {
|
|||
Descent,
|
||||
}
|
||||
|
||||
///<span data-del-macro-root></span> Creates a [`Txt`] by formatting using the [`format_args!`] syntax.
|
||||
///
|
||||
/// Note that this behaves like a [`format!`] for [`Txt`], but it can be more performant because the
|
||||
/// text type can represent `&'static str` and can i
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use zero_ui_core::text::formatx;
|
||||
/// let text = formatx!("Hello {}", "World!");
|
||||
/// ```
|
||||
///
|
||||
/// [`Txt`]: crate::text::Txt
|
||||
#[macro_export]
|
||||
macro_rules! formatx {
|
||||
($($tt:tt)*) => {
|
||||
$crate::text::Txt::from_fmt(format_args!($($tt)*))
|
||||
};
|
||||
}
|
||||
#[doc(inline)]
|
||||
pub use crate::formatx;
|
||||
use crate::var::{IntoVar, LocalVar};
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -35,6 +35,7 @@ pub use iter::TreeFilter;
|
|||
|
||||
mod hit;
|
||||
pub(crate) use hit::HitTestClips;
|
||||
use zero_ui_txt::formatx;
|
||||
|
||||
pub use self::hit::RelativeHitZ;
|
||||
use self::{access::AccessEnabled, iter::TreeIterator};
|
||||
|
@ -1056,13 +1057,13 @@ impl WidgetInfo {
|
|||
let id = self.id();
|
||||
let name = id.name();
|
||||
if !name.is_empty() {
|
||||
return crate::formatx!("{mod_ident}!({name:?})");
|
||||
return formatx!("{mod_ident}!({name:?})");
|
||||
} else {
|
||||
return crate::formatx!("{mod_ident}!({})", id.sequential());
|
||||
return formatx!("{mod_ident}!({})", id.sequential());
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::formatx!("{}", self.id())
|
||||
formatx!("{}", self.id())
|
||||
}
|
||||
|
||||
/// Full path to this widget with [`interactivity`] values.
|
||||
|
|
|
@ -1221,9 +1221,9 @@ impl PartialEq for AccessStateSource {
|
|||
impl From<&AccessStateSource> for AccessState {
|
||||
fn from(value: &AccessStateSource) -> Self {
|
||||
match value {
|
||||
AccessStateSource::Label(l) => AccessState::Label(l.to_string()),
|
||||
AccessStateSource::Placeholder(p) => AccessState::Placeholder(p.to_string()),
|
||||
AccessStateSource::ValueText(v) => AccessState::ValueText(v.to_string()),
|
||||
AccessStateSource::Label(l) => AccessState::Label(l.clone()),
|
||||
AccessStateSource::Placeholder(p) => AccessState::Placeholder(p.clone()),
|
||||
AccessStateSource::ValueText(v) => AccessState::ValueText(v.clone()),
|
||||
AccessStateSource::ScrollHorizontal(x) => AccessState::ScrollHorizontal(x.get().0),
|
||||
AccessStateSource::ScrollVertical(y) => AccessState::ScrollVertical(y.get().0),
|
||||
}
|
||||
|
|
|
@ -413,7 +413,7 @@ impl HeadedCtrl {
|
|||
|
||||
if let Some(title) = self.vars.title().get_new() {
|
||||
self.update_gen(move |view| {
|
||||
let _: Ignore = view.set_title(title.into_owned());
|
||||
let _: Ignore = view.set_title(title);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1026,7 +1026,7 @@ impl HeadedCtrl {
|
|||
|
||||
let request = WindowRequest {
|
||||
id: crate::app::view_process::ApiWindowId::from_raw(WINDOW.id().get()),
|
||||
title: self.vars.title().get().to_string(),
|
||||
title: self.vars.title().get(),
|
||||
state: state.clone(),
|
||||
kiosk: self.kiosk.is_some(),
|
||||
default_position: system_pos,
|
||||
|
@ -1154,7 +1154,7 @@ impl HeadedCtrl {
|
|||
|
||||
let request = WindowRequest {
|
||||
id: crate::app::view_process::ApiWindowId::from_raw(WINDOW.id().get()),
|
||||
title: self.vars.title().get_string(),
|
||||
title: self.vars.title().get(),
|
||||
state: self.state.clone().unwrap(),
|
||||
kiosk: self.kiosk.is_some(),
|
||||
default_position: false,
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::{fmt, mem};
|
|||
|
||||
use parking_lot::Mutex;
|
||||
use rayon::prelude::*;
|
||||
use zero_ui_txt::{formatx, Txt};
|
||||
|
||||
use super::commands::WindowCommands;
|
||||
use super::*;
|
||||
|
@ -154,13 +155,13 @@ impl WindowsService {
|
|||
self.frame_images.push(img.clone());
|
||||
img.read_only()
|
||||
}
|
||||
Err(_) => var(Img::dummy(Some(format!("{}", WindowNotFound(window_id))))).read_only(),
|
||||
Err(_) => var(Img::dummy(Some(formatx!("{}", WindowNotFound(window_id))))).read_only(),
|
||||
}
|
||||
} else {
|
||||
var(Img::dummy(Some(format!("window `{window_id}` is headless without renderer")))).read_only()
|
||||
var(Img::dummy(Some(formatx!("window `{window_id}` is headless without renderer")))).read_only()
|
||||
}
|
||||
} else {
|
||||
var(Img::dummy(Some(format!("{}", WindowNotFound(window_id))))).read_only()
|
||||
var(Img::dummy(Some(formatx!("{}", WindowNotFound(window_id))))).read_only()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1127,10 +1128,10 @@ impl WINDOWS {
|
|||
WINDOWS_SV.write().view_window_task(window_id, move |win| match win {
|
||||
Some(win) => {
|
||||
if let Err(e) = win.message_dialog(dialog, responder.clone()) {
|
||||
responder.respond(view_process::MsgDialogResponse::Error(format!("{e}")))
|
||||
responder.respond(view_process::MsgDialogResponse::Error(formatx!("{e}")))
|
||||
}
|
||||
}
|
||||
None => responder.respond(view_process::MsgDialogResponse::Error("native window not found".to_owned())),
|
||||
None => responder.respond(view_process::MsgDialogResponse::Error(Txt::from_static("native window not found"))),
|
||||
});
|
||||
rsp
|
||||
}
|
||||
|
@ -1148,10 +1149,10 @@ impl WINDOWS {
|
|||
WINDOWS_SV.write().view_window_task(window_id, move |win| match win {
|
||||
Some(win) => {
|
||||
if let Err(e) = win.file_dialog(dialog, responder.clone()) {
|
||||
responder.respond(view_process::FileDialogResponse::Error(format!("{e}")))
|
||||
responder.respond(view_process::FileDialogResponse::Error(formatx!("{e}")))
|
||||
}
|
||||
}
|
||||
None => responder.respond(view_process::FileDialogResponse::Error("native window not found".to_owned())),
|
||||
None => responder.respond(view_process::FileDialogResponse::Error(Txt::from_static("native window not found"))),
|
||||
});
|
||||
rsp
|
||||
}
|
||||
|
|
|
@ -626,3 +626,21 @@ impl<T: ToString> ToText for T {
|
|||
self.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
///<span data-del-macro-root></span> Creates a [`Txt`] by formatting using the [`format_args!`] syntax.
|
||||
///
|
||||
/// Note that this behaves like a [`format!`] for [`Txt`], but it can be more performant because the
|
||||
/// text type can represent `&'static str` and can i
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use zero_ui_core::text::formatx;
|
||||
/// let text = formatx!("Hello {}", "World!");
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! formatx {
|
||||
($($tt:tt)*) => {
|
||||
$crate::Txt::from_fmt(format_args!($($tt)*))
|
||||
};
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ webrender_api = { git = "https://github.com/servo/webrender.git", rev = "8589629
|
|||
euclid = { version = "0.22.6", features = ["serde", "bytemuck"] } # same version as webrender, but with bytemuck
|
||||
|
||||
zero-ui-units = { path = "../zero-ui-units" }
|
||||
zero-ui-txt = { path = "../zero-ui-txt" }
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_bytes = "0.11"
|
||||
|
|
|
@ -5,6 +5,7 @@ use std::{num::NonZeroU32, ops};
|
|||
use bitflags::bitflags;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::{PxRect, PxSize, PxTransform};
|
||||
|
||||
/// Accessibility role of a node in the accessibility tree.
|
||||
|
@ -239,7 +240,7 @@ pub enum AccessState {
|
|||
Invalid(Invalid),
|
||||
|
||||
/// Defines a string value that labels the widget.
|
||||
Label(String),
|
||||
Label(Txt),
|
||||
|
||||
/// Defines the hierarchical level of an widget within a structure.
|
||||
Level(NonZeroU32),
|
||||
|
@ -250,7 +251,7 @@ pub enum AccessState {
|
|||
/// Indicates whether the widget's orientation is horizontal, vertical, or unknown/ambiguous.
|
||||
Orientation(Orientation),
|
||||
/// Short hint (a word or short phrase) intended to help the user with data entry when a form control has no value.
|
||||
Placeholder(String),
|
||||
Placeholder(Txt),
|
||||
/// Indicates that the widget is not editable, but is otherwise operable.
|
||||
ReadOnly,
|
||||
/// Indicates that user input is required on the widget before a form may be submitted.
|
||||
|
@ -268,7 +269,7 @@ pub enum AccessState {
|
|||
/// Defines a human readable version of the [`Value`].
|
||||
///
|
||||
/// [`Value`]: Self::Value
|
||||
ValueText(String),
|
||||
ValueText(Txt),
|
||||
|
||||
/// Indicate that the widget can change.
|
||||
Live {
|
||||
|
@ -479,7 +480,7 @@ pub enum AccessCmd {
|
|||
Scroll(ScrollCmd),
|
||||
|
||||
/// Insert the text.
|
||||
ReplaceSelectedText(String),
|
||||
ReplaceSelectedText(Txt),
|
||||
|
||||
/// Set the text selection.
|
||||
///
|
||||
|
@ -495,7 +496,7 @@ pub enum AccessCmd {
|
|||
|
||||
/// Replace the value of the control with the specified value and
|
||||
/// reset the selection, if applicable.
|
||||
SetString(String),
|
||||
SetString(Txt),
|
||||
|
||||
/// Replace the value of the control with the specified value and
|
||||
/// reset the selection, if applicable.
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
//! Axis analog device types.
|
|
@ -3,6 +3,7 @@
|
|||
use std::{fmt, ops};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
/// Custom serialized data, in a format defined by the extension.
|
||||
///
|
||||
|
@ -25,7 +26,7 @@ impl ApiExtensionPayload {
|
|||
if let Some((id, error)) = self.parse_invalid_request() {
|
||||
Err(ApiExtensionRecvError::InvalidRequest {
|
||||
extension_id: id,
|
||||
error: error.to_owned(),
|
||||
error: Txt::from_str(error),
|
||||
})
|
||||
} else if let Some(id) = self.parse_unknown_extension() {
|
||||
Err(ApiExtensionRecvError::UnknownExtension { extension_id: id })
|
||||
|
@ -116,17 +117,17 @@ impl fmt::Debug for ApiExtensionPayload {
|
|||
/// by using the extension payload
|
||||
#[derive(Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ApiExtensionName {
|
||||
name: String,
|
||||
name: Txt,
|
||||
}
|
||||
impl ApiExtensionName {
|
||||
/// New from unique name.
|
||||
///
|
||||
/// The name must contain at least 1 characters, and match the pattern `[a-zA-Z][a-zA-Z0-9-_.]`.
|
||||
pub fn new(name: impl Into<String>) -> Result<Self, ApiExtensionNameError> {
|
||||
pub fn new(name: impl Into<Txt>) -> Result<Self, ApiExtensionNameError> {
|
||||
let name = name.into();
|
||||
Self::new_impl(name)
|
||||
}
|
||||
fn new_impl(name: String) -> Result<ApiExtensionName, ApiExtensionNameError> {
|
||||
fn new_impl(name: Txt) -> Result<ApiExtensionName, ApiExtensionNameError> {
|
||||
if name.is_empty() {
|
||||
return Err(ApiExtensionNameError::NameCannotBeEmpty);
|
||||
}
|
||||
|
@ -316,7 +317,7 @@ pub enum ApiExtensionRecvError {
|
|||
/// Is `INVALID` only if error message is corrupted.
|
||||
extension_id: ApiExtensionId,
|
||||
/// Message from the view-process.
|
||||
error: String,
|
||||
error: Txt,
|
||||
},
|
||||
/// Failed to deserialize to the expected response type.
|
||||
Deserialize(bincode::Error),
|
||||
|
|
|
@ -8,6 +8,8 @@ use std::{
|
|||
#[cfg(feature = "ipc")]
|
||||
use std::time::Duration;
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
use crate::{ipc, AnyResult, Event, Request, Response, ViewConfig, ViewProcessGen, ViewProcessOffline, VpResult};
|
||||
|
||||
/// The listener returns the closure on join for reuse in respawn.
|
||||
|
@ -208,8 +210,8 @@ impl Controller {
|
|||
// create process and spawn it, unless is running in same process mode.
|
||||
let process = if ViewConfig::is_awaiting_same_process() {
|
||||
ViewConfig::set_same_process(ViewConfig {
|
||||
version: crate::VERSION.to_owned(),
|
||||
server_name: init.name().to_owned(),
|
||||
version: crate::VERSION.into(),
|
||||
server_name: Txt::from_str(init.name()),
|
||||
headless,
|
||||
});
|
||||
None
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use std::{fmt, path::PathBuf};
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
use crate::{image::ImageId, ipc::IpcBytes};
|
||||
|
||||
/// Clipboard data.
|
||||
|
@ -10,7 +12,7 @@ pub enum ClipboardData {
|
|||
/// Text string.
|
||||
///
|
||||
/// View-process can convert between [`String`] and the text formats of the platform.
|
||||
Text(String),
|
||||
Text(Txt),
|
||||
/// Image data.
|
||||
///
|
||||
/// View-process reads from clipboard in any format supported and starts an image decode task
|
||||
|
@ -23,7 +25,7 @@ pub enum ClipboardData {
|
|||
/// Any data format supported only by the specific view-process implementation.
|
||||
Extension {
|
||||
/// Type key, must be in a format defined by the view-process.
|
||||
data_type: String,
|
||||
data_type: Txt,
|
||||
/// The raw data.
|
||||
data: IpcBytes,
|
||||
},
|
||||
|
@ -39,7 +41,7 @@ pub enum ClipboardType {
|
|||
/// A [`ClipboardData::FileList`].
|
||||
FileList,
|
||||
/// A [`ClipboardData::Extension`].
|
||||
Extension(String),
|
||||
Extension(Txt),
|
||||
}
|
||||
|
||||
/// Clipboard read/write error.
|
||||
|
@ -52,7 +54,7 @@ pub enum ClipboardError {
|
|||
/// Other error.
|
||||
///
|
||||
/// The string can be a debug description of the error, only suitable for logging.
|
||||
Other(String),
|
||||
Other(Txt),
|
||||
}
|
||||
impl fmt::Display for ClipboardError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::{fmt, time::Duration};
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::{Dip, DipSize};
|
||||
|
||||
/// System settings needed for implementing double/triple clicks.
|
||||
|
@ -116,7 +117,7 @@ impl Default for AnimationsConfig {
|
|||
#[derive(Debug, Clone, Serialize, PartialEq, Eq, Deserialize, Default)]
|
||||
pub struct LocaleConfig {
|
||||
/// BCP-47 language tags, if the locale can be obtained.
|
||||
pub langs: Vec<String>,
|
||||
pub langs: Vec<Txt>,
|
||||
}
|
||||
|
||||
/// Text anti-aliasing.
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
crate::declare_id! {
|
||||
/// Identifies an ongoing async native dialog with the user.
|
||||
pub struct DialogId(_);
|
||||
|
@ -11,9 +13,9 @@ crate::declare_id! {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct MsgDialog {
|
||||
/// Message dialog window title.
|
||||
pub title: String,
|
||||
pub title: Txt,
|
||||
/// Message text.
|
||||
pub message: String,
|
||||
pub message: Txt,
|
||||
/// Kind of message.
|
||||
pub icon: MsgDialogIcon,
|
||||
/// Message buttons.
|
||||
|
@ -22,8 +24,8 @@ pub struct MsgDialog {
|
|||
impl Default for MsgDialog {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
title: String::new(),
|
||||
message: String::new(),
|
||||
title: Txt::from_str(""),
|
||||
message: Txt::from_str(""),
|
||||
icon: MsgDialogIcon::Info,
|
||||
buttons: MsgDialogButtons::Ok,
|
||||
}
|
||||
|
@ -75,18 +77,18 @@ pub enum MsgDialogResponse {
|
|||
///
|
||||
/// The associated string may contain debug information, caller should assume that native file dialogs
|
||||
/// are not available for the given window ID at the current view-process instance.
|
||||
Error(String),
|
||||
Error(Txt),
|
||||
}
|
||||
|
||||
/// Defines a native file dialog.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
pub struct FileDialog {
|
||||
/// Dialog window title.
|
||||
pub title: String,
|
||||
pub title: Txt,
|
||||
/// Selected directory when the dialog opens.
|
||||
pub starting_dir: PathBuf,
|
||||
/// Starting file name.
|
||||
pub starting_name: String,
|
||||
pub starting_name: Txt,
|
||||
/// File extension filters.
|
||||
///
|
||||
/// Syntax:
|
||||
|
@ -99,7 +101,7 @@ pub struct FileDialog {
|
|||
/// not glob patterns, they must be an extension (without the dot prefix) or `*` for all files.
|
||||
///
|
||||
/// [`push_filter`]: Self::push_filter
|
||||
pub filters: String,
|
||||
pub filters: Txt,
|
||||
|
||||
/// Defines the file dialog looks and what kind of result is expected.
|
||||
pub kind: FileDialogKind,
|
||||
|
@ -213,10 +215,10 @@ impl FileDialog {
|
|||
impl Default for FileDialog {
|
||||
fn default() -> Self {
|
||||
FileDialog {
|
||||
title: String::new(),
|
||||
title: Txt::from_str(""),
|
||||
starting_dir: PathBuf::new(),
|
||||
starting_name: String::new(),
|
||||
filters: String::new(),
|
||||
starting_name: Txt::from_str(""),
|
||||
filters: Txt::from_str(""),
|
||||
kind: FileDialogKind::OpenFile,
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +252,7 @@ pub enum FileDialogResponse {
|
|||
///
|
||||
/// The associated string may contain debug information, caller should assume that native file dialogs
|
||||
/// are not available for the given window ID at the current view-process instance.
|
||||
Error(String),
|
||||
Error(Txt),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -260,10 +262,10 @@ mod tests {
|
|||
#[test]
|
||||
fn file_filters() {
|
||||
let mut dlg = FileDialog {
|
||||
title: "".to_owned(),
|
||||
title: "".into(),
|
||||
starting_dir: "".into(),
|
||||
starting_name: "".to_owned(),
|
||||
filters: "".to_owned(),
|
||||
starting_name: "".into(),
|
||||
filters: "".into(),
|
||||
kind: FileDialogKind::OpenFile,
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
use std::fmt;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
use crate::ipc::IpcBytes;
|
||||
use zero_ui_units::{Px, PxSize};
|
||||
|
@ -112,17 +113,17 @@ pub enum ImageDataFormat {
|
|||
|
||||
/// The image is encoded, a file extension that maybe identifies
|
||||
/// the format is known.
|
||||
FileExtension(String),
|
||||
FileExtension(Txt),
|
||||
|
||||
/// The image is encoded, MIME type that maybe identifies the format is known.
|
||||
MimeType(String),
|
||||
MimeType(Txt),
|
||||
|
||||
/// The image is encoded, a decoder will be selected using the "magic number"
|
||||
/// on the beginning of the bytes buffer.
|
||||
Unknown,
|
||||
}
|
||||
impl From<String> for ImageDataFormat {
|
||||
fn from(ext_or_mime: String) -> Self {
|
||||
impl From<Txt> for ImageDataFormat {
|
||||
fn from(ext_or_mime: Txt) -> Self {
|
||||
if ext_or_mime.contains('/') {
|
||||
ImageDataFormat::MimeType(ext_or_mime)
|
||||
} else {
|
||||
|
@ -132,7 +133,7 @@ impl From<String> for ImageDataFormat {
|
|||
}
|
||||
impl From<&str> for ImageDataFormat {
|
||||
fn from(ext_or_mime: &str) -> Self {
|
||||
ext_or_mime.to_owned().into()
|
||||
Txt::from_str(ext_or_mime).into()
|
||||
}
|
||||
}
|
||||
impl From<PxSize> for ImageDataFormat {
|
||||
|
|
|
@ -12,6 +12,7 @@ use flume::unbounded as channel;
|
|||
|
||||
use parking_lot::Mutex;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
pub(crate) type IpcResult<T> = std::result::Result<T, Disconnected>;
|
||||
|
||||
|
@ -205,13 +206,16 @@ impl std::error::Error for Disconnected {}
|
|||
#[cfg(feature = "ipc")]
|
||||
pub(crate) struct AppInit {
|
||||
server: IpcOneShotServer<AppInitMsg>,
|
||||
name: String,
|
||||
name: Txt,
|
||||
}
|
||||
#[cfg(feature = "ipc")]
|
||||
impl AppInit {
|
||||
pub fn new() -> Self {
|
||||
let (server, name) = IpcOneShotServer::new().expect("failed to create init channel");
|
||||
AppInit { server, name }
|
||||
AppInit {
|
||||
server,
|
||||
name: Txt::from_str(&name),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unique name for the view-process to find this channel.
|
||||
|
@ -246,10 +250,10 @@ impl AppInit {
|
|||
|
||||
/// Start the view-process server and waits for `(request, response, event)`.
|
||||
#[cfg(feature = "ipc")]
|
||||
pub fn connect_view_process(server_name: String) -> IpcResult<ViewChannels> {
|
||||
pub fn connect_view_process(server_name: Txt) -> IpcResult<ViewChannels> {
|
||||
let _s = tracing::trace_span!("connect_view_process").entered();
|
||||
|
||||
let app_init_sender = IpcSender::connect(server_name).expect("failed to connect to init channel");
|
||||
let app_init_sender = IpcSender::connect(server_name.into_owned()).expect("failed to connect to init channel");
|
||||
|
||||
let (req_sender, req_recv) = channel().map_err(handle_io_error)?;
|
||||
// Large messages can only be received in a receiver created in the same process that is receiving (on Windows)
|
||||
|
@ -283,7 +287,7 @@ pub(crate) struct AppInit {
|
|||
// )
|
||||
#[allow(clippy::type_complexity)]
|
||||
init: flume::Receiver<AppInitMsg>,
|
||||
name: String,
|
||||
name: Txt,
|
||||
}
|
||||
#[cfg(not(feature = "ipc"))]
|
||||
mod name_map {
|
||||
|
@ -295,7 +299,7 @@ mod name_map {
|
|||
|
||||
use super::AppInitMsg;
|
||||
|
||||
type Map = Mutex<HashMap<String, flume::Sender<AppInitMsg>>>;
|
||||
type Map = Mutex<HashMap<Txt, flume::Sender<AppInitMsg>>>;
|
||||
|
||||
pub fn get() -> &'static Map {
|
||||
static mut MAP: MaybeUninit<Map> = MaybeUninit::uninit();
|
||||
|
@ -349,7 +353,7 @@ impl AppInit {
|
|||
|
||||
/// Start the view-process server and waits for `(request, response, event)`.
|
||||
#[cfg(not(feature = "ipc"))]
|
||||
pub fn connect_view_process(server_name: String) -> IpcResult<ViewChannels> {
|
||||
pub fn connect_view_process(server_name: Txt) -> IpcResult<ViewChannels> {
|
||||
let app_init_sender = name_map::get().lock().unwrap().remove(&server_name).unwrap();
|
||||
|
||||
let (req_sender, req_recv) = channel();
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//!: Keyboard types.
|
||||
|
||||
use std::{fmt, mem, sync::Arc};
|
||||
use std::{fmt, mem};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
/// Contains the platform-native physical key identifier
|
||||
///
|
||||
|
@ -767,7 +768,7 @@ pub enum Key {
|
|||
/// A key string that corresponds to the character typed by the user, taking into account the
|
||||
/// user’s current locale setting, and any system-level keyboard mapping overrides that are in
|
||||
/// effect.
|
||||
Str(Arc<str>),
|
||||
Str(Txt),
|
||||
|
||||
/// This variant is used when the key cannot be translated to any other variant.
|
||||
///
|
||||
|
@ -780,6 +781,7 @@ pub enum Key {
|
|||
/// - **Web:** Always contains `None`
|
||||
Dead(Option<char>),
|
||||
|
||||
/* SAFETY, no associated data after Alt, see `Key::all_named` */
|
||||
/// The `Alt` (Alternative) key.
|
||||
///
|
||||
/// This key enables the alternate modifier function for interpreting concurrent or subsequent
|
||||
|
@ -1948,9 +1950,9 @@ impl Key {
|
|||
pub fn all_named() -> impl ExactSizeIterator<Item = Key> + DoubleEndedIterator {
|
||||
unsafe {
|
||||
// SAFETY: this is safe because all variants from `Alt` are without associated data.
|
||||
let s: (u16, [u8; 22]) = mem::transmute(Key::Alt);
|
||||
let e: (u16, [u8; 22]) = mem::transmute(Key::F35);
|
||||
(s.0..=e.0).map(|n| mem::transmute((n, [0u8; 22])))
|
||||
let s: (u16, [u8; 38]) = mem::transmute(Key::Alt);
|
||||
let e: (u16, [u8; 38]) = mem::transmute(Key::F35);
|
||||
(s.0..=e.0).map(|n| mem::transmute((n, [0u8; 38])))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1969,7 +1971,7 @@ impl fmt::Debug for Key {
|
|||
let name = self.name();
|
||||
match self {
|
||||
Self::Char(c) => write!(f, "{name}({c:?})"),
|
||||
Self::Str(s) => write!(f, "{name}({:?})", s.as_ref()),
|
||||
Self::Str(s) => write!(f, "{name}({:?})", s.as_str()),
|
||||
Self::Dead(c) => write!(f, "{name}({c:?})"),
|
||||
_ => write!(f, "{name}"),
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ use serde::{Deserialize, Serialize};
|
|||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub mod access;
|
||||
pub mod analog;
|
||||
pub mod api_extension;
|
||||
pub mod clipboard;
|
||||
pub mod config;
|
||||
|
@ -46,6 +45,7 @@ pub use app_process::*;
|
|||
|
||||
mod view_process;
|
||||
pub use view_process::*;
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
use std::fmt;
|
||||
use webrender_api::{FontInstanceKey, FontKey, ImageKey};
|
||||
|
@ -288,7 +288,7 @@ declare_api! {
|
|||
pub fn close_window(&mut self, id: WindowId);
|
||||
|
||||
/// Set window title.
|
||||
pub fn set_title(&mut self, id: WindowId, title: String);
|
||||
pub fn set_title(&mut self, id: WindowId, title: Txt);
|
||||
|
||||
/// Set window visible.
|
||||
pub fn set_visible(&mut self, id: WindowId, visible: bool);
|
||||
|
@ -393,12 +393,12 @@ declare_api! {
|
|||
/// Returns a list of image decoders supported by this implementation.
|
||||
///
|
||||
/// Each string is the lower-case file extension.
|
||||
pub fn image_decoders(&mut self) -> Vec<String>;
|
||||
pub fn image_decoders(&mut self) -> Vec<Txt>;
|
||||
|
||||
/// Returns a list of image encoders supported by this implementation.
|
||||
///
|
||||
/// Each string is the lower-case file extension.
|
||||
pub fn image_encoders(&mut self) -> Vec<String>;
|
||||
pub fn image_encoders(&mut self) -> Vec<Txt>;
|
||||
|
||||
/// Encode the image into the `format`.
|
||||
///
|
||||
|
@ -408,7 +408,7 @@ declare_api! {
|
|||
/// [`Event::ImageEncoded`] or [`Event::ImageEncodeError`].
|
||||
///
|
||||
/// [`image_encoders`]: Api::image_encoders
|
||||
pub fn encode_image(&mut self, id: ImageId, format: String);
|
||||
pub fn encode_image(&mut self, id: ImageId, format: Txt);
|
||||
|
||||
/// Add a raw font resource to the window renderer.
|
||||
///
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{fmt, path::PathBuf, sync::Arc};
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::{DipPoint, PxRect, PxSize};
|
||||
|
||||
macro_rules! declare_id {
|
||||
|
@ -179,7 +180,7 @@ pub enum Event {
|
|||
/// Id from the request.
|
||||
id: WindowId,
|
||||
/// Error message.
|
||||
error: String,
|
||||
error: Txt,
|
||||
},
|
||||
|
||||
/// A frame finished rendering.
|
||||
|
@ -261,7 +262,7 @@ pub enum Event {
|
|||
/// This is usually the `key_modified` char, but is also `'\r'` for `Key::Enter`. On Windows when a dead key was
|
||||
/// pressed earlier but cannot be combined with the character from this key press, the produced text
|
||||
/// will consist of two characters: the dead-key-character followed by the character resulting from this key press.
|
||||
text: String,
|
||||
text: Txt,
|
||||
},
|
||||
/// IME composition event.
|
||||
Ime {
|
||||
|
@ -413,14 +414,14 @@ pub enum Event {
|
|||
/// The image that failed to decode.
|
||||
image: ImageId,
|
||||
/// The error message.
|
||||
error: String,
|
||||
error: Txt,
|
||||
},
|
||||
/// An image finished encoding.
|
||||
ImageEncoded {
|
||||
/// The image that finished encoding.
|
||||
image: ImageId,
|
||||
/// The format of the encoded data.
|
||||
format: String,
|
||||
format: Txt,
|
||||
/// The encoded image data.
|
||||
data: IpcBytes,
|
||||
},
|
||||
|
@ -429,9 +430,9 @@ pub enum Event {
|
|||
/// The image that failed to encode.
|
||||
image: ImageId,
|
||||
/// The encoded format that was requested.
|
||||
format: String,
|
||||
format: Txt,
|
||||
/// The error message.
|
||||
error: String,
|
||||
error: Txt,
|
||||
},
|
||||
|
||||
/// An image generated from a rendered frame is ready.
|
||||
|
|
|
@ -3,6 +3,8 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
use crate::{MODE_VAR, SERVER_NAME_VAR, VERSION_VAR};
|
||||
|
||||
/// Configuration for starting a view-process.
|
||||
|
@ -11,13 +13,13 @@ pub struct ViewConfig {
|
|||
/// The [`VERSION`] of the API crate in the app-process.
|
||||
///
|
||||
/// [`VERSION`]: crate::VERSION
|
||||
pub version: String,
|
||||
pub version: Txt,
|
||||
|
||||
/// Name of the initial channel used in [`connect_view_process`] to setup the connections to the
|
||||
/// client app-process.
|
||||
///
|
||||
/// [`connect_view_process`]: crate::ipc::connect_view_process
|
||||
pub server_name: String,
|
||||
pub server_name: Txt,
|
||||
|
||||
/// If the server should consider all window requests, headless window requests.
|
||||
pub headless: bool,
|
||||
|
@ -33,8 +35,8 @@ impl ViewConfig {
|
|||
if let (Ok(version), Ok(server_name)) = (env::var(VERSION_VAR), env::var(SERVER_NAME_VAR)) {
|
||||
let headless = env::var(MODE_VAR).map(|m| m == "headless").unwrap_or(false);
|
||||
Some(ViewConfig {
|
||||
version,
|
||||
server_name,
|
||||
version: Txt::from_str(&version),
|
||||
server_name: Txt::from_str(&server_name),
|
||||
headless,
|
||||
})
|
||||
} else {
|
||||
|
@ -99,8 +101,8 @@ impl ViewConfig {
|
|||
);
|
||||
|
||||
ViewConfig {
|
||||
version: config[0].to_owned(),
|
||||
server_name: config[1].to_owned(),
|
||||
version: Txt::from_str(config[0]),
|
||||
server_name: Txt::from_str(config[1]),
|
||||
headless: config[2] == "true",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::fmt;
|
|||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use webrender_api::{ColorF, Epoch, PipelineId, RenderReasons};
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
use crate::{
|
||||
access::AccessNodeId,
|
||||
|
@ -121,7 +122,7 @@ pub struct HeadlessRequest {
|
|||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MonitorInfo {
|
||||
/// Readable name of the monitor.
|
||||
pub name: String,
|
||||
pub name: Txt,
|
||||
/// Top-left offset of the monitor region in the virtual screen, in pixels.
|
||||
pub position: PxPoint,
|
||||
/// Width/height of the monitor region in the virtual screen, in pixels.
|
||||
|
@ -395,7 +396,7 @@ pub struct WindowRequest {
|
|||
/// ID that will identify the new window.
|
||||
pub id: WindowId,
|
||||
/// Title text.
|
||||
pub title: String,
|
||||
pub title: Txt,
|
||||
|
||||
/// Window state, position, size and restore rectangle.
|
||||
pub state: WindowStateAll,
|
||||
|
|
|
@ -67,6 +67,7 @@ swgl = { git = "https://github.com/servo/webrender.git", rev = "858962993c5e0606
|
|||
|
||||
zero-ui-view-api = { path = "../zero-ui-view-api", default-features = false }
|
||||
zero-ui-units = { path = "../zero-ui-units" }
|
||||
zero-ui-txt = { path = "../zero-ui-txt" }
|
||||
|
||||
tracing = "0.1"
|
||||
gleam = "0.15.0" # matches webrender
|
||||
|
|
|
@ -404,10 +404,11 @@ pub(crate) fn locale_config() -> LocaleConfig {
|
|||
Foundation::FALSE,
|
||||
Globalization::{GetUserPreferredUILanguages, MUI_LANGUAGE_NAME},
|
||||
};
|
||||
use zero_ui_txt::Txt;
|
||||
|
||||
// Try newer WinRT COM API (Windows8+)
|
||||
if let Ok(r) = GlobalizationPreferences::Languages() {
|
||||
let r: Vec<String> = r.into_iter().map(|l| l.to_string_lossy()).collect();
|
||||
let r: Vec<_> = r.into_iter().map(|l| Txt::from_str(&l.to_string_lossy())).collect();
|
||||
if !r.is_empty() {
|
||||
return LocaleConfig { langs: r };
|
||||
}
|
||||
|
@ -436,6 +437,6 @@ pub(crate) fn locale_config() -> LocaleConfig {
|
|||
buffer.pop();
|
||||
|
||||
LocaleConfig {
|
||||
langs: String::from_utf16_lossy(&buffer).split('\0').map(|x| x.to_string()).collect(),
|
||||
langs: String::from_utf16_lossy(&buffer).split('\0').map(Txt::from_str).collect(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::{fmt, sync::Arc};
|
|||
|
||||
use webrender::api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat};
|
||||
use winit::window::Icon;
|
||||
use zero_ui_txt::{formatx, ToText, Txt};
|
||||
use zero_ui_units::{Px, PxPoint, PxSize};
|
||||
use zero_ui_view_api::{
|
||||
image::{ImageDataFormat, ImageDownscale, ImageId, ImageLoadedData, ImageMaskMode, ImagePpi, ImageRequest},
|
||||
|
@ -60,7 +61,7 @@ impl ImageCache {
|
|||
ImageDataFormat::Bgra8 { size, ppi } => {
|
||||
let expected_len = size.width.0 as usize * size.height.0 as usize * 4;
|
||||
if data.len() != expected_len {
|
||||
Err(format!(
|
||||
Err(formatx!(
|
||||
"pixels.len() is not width * height * 4, expected {expected_len}, found {}",
|
||||
data.len()
|
||||
))
|
||||
|
@ -80,7 +81,7 @@ impl ImageCache {
|
|||
ImageDataFormat::A8 { size } => {
|
||||
let expected_len = size.width.0 as usize * size.height.0 as usize;
|
||||
if data.len() != expected_len {
|
||||
Err(format!(
|
||||
Err(formatx!(
|
||||
"pixels.len() is not width * height, expected {expected_len}, found {}",
|
||||
data.len()
|
||||
))
|
||||
|
@ -101,7 +102,7 @@ impl ImageCache {
|
|||
Ok((fmt, size)) => {
|
||||
let decoded_len = size.width.0 as u64 * size.height.0 as u64 * 4;
|
||||
if decoded_len > max_decoded_len {
|
||||
Err(format!(
|
||||
Err(formatx!(
|
||||
"image {size:?} needs to allocate {decoded_len} bytes, but max allowed size is {max_decoded_len} bytes",
|
||||
))
|
||||
} else {
|
||||
|
@ -113,7 +114,7 @@ impl ImageCache {
|
|||
}));
|
||||
match Self::image_decode(&data[..], fmt, downscale) {
|
||||
Ok(img) => Ok(Self::convert_decoded(img, mask)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(e.to_text()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +173,7 @@ impl ImageCache {
|
|||
size = Some(s);
|
||||
None
|
||||
}
|
||||
ImageDataFormat::FileExtension(ext) => image::ImageFormat::from_extension(ext),
|
||||
ImageDataFormat::FileExtension(ext) => image::ImageFormat::from_extension(ext.as_str()),
|
||||
ImageDataFormat::MimeType(t) => t.strip_prefix("image/").and_then(image::ImageFormat::from_extension),
|
||||
ImageDataFormat::Unknown => None,
|
||||
};
|
||||
|
@ -194,7 +195,7 @@ impl ImageCache {
|
|||
if let Some(s) = size {
|
||||
let decoded_len = s.width.0 as u64 * s.height.0 as u64 * 4;
|
||||
if decoded_len > max_decoded_len {
|
||||
let error = format!(
|
||||
let error = formatx!(
|
||||
"image {size:?} needs to allocate {decoded_len} bytes, but max allowed size is {max_decoded_len} bytes",
|
||||
);
|
||||
let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError { image: id, error }));
|
||||
|
@ -229,7 +230,7 @@ impl ImageCache {
|
|||
Err(e) => {
|
||||
let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError {
|
||||
image: id,
|
||||
error: e.to_string(),
|
||||
error: e.to_text(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +248,7 @@ impl ImageCache {
|
|||
} else {
|
||||
let _ = app_sender.send(AppEvent::Notify(Event::ImageLoadError {
|
||||
image: id,
|
||||
error: "unknown format".to_string(),
|
||||
error: Txt::from_static("unknown format"),
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
@ -287,9 +288,9 @@ impl ImageCache {
|
|||
let _ = self.app_sender.send(AppEvent::Notify(Event::ImageLoaded(data)));
|
||||
}
|
||||
|
||||
fn get_format_and_size(fmt: &ImageDataFormat, data: &[u8]) -> Result<(image::ImageFormat, PxSize), String> {
|
||||
fn get_format_and_size(fmt: &ImageDataFormat, data: &[u8]) -> Result<(image::ImageFormat, PxSize), Txt> {
|
||||
let fmt = match fmt {
|
||||
ImageDataFormat::FileExtension(ext) => image::ImageFormat::from_extension(ext),
|
||||
ImageDataFormat::FileExtension(ext) => image::ImageFormat::from_extension(ext.as_str()),
|
||||
ImageDataFormat::MimeType(t) => t.strip_prefix("image/").and_then(image::ImageFormat::from_extension),
|
||||
ImageDataFormat::Unknown => None,
|
||||
ImageDataFormat::Bgra8 { .. } => unreachable!(),
|
||||
|
@ -300,7 +301,7 @@ impl ImageCache {
|
|||
Some(fmt) => image::io::Reader::with_format(std::io::Cursor::new(data), fmt),
|
||||
None => image::io::Reader::new(std::io::Cursor::new(data))
|
||||
.with_guessed_format()
|
||||
.map_err(|e| e.to_string())?,
|
||||
.map_err(|e| e.to_text())?,
|
||||
};
|
||||
|
||||
match reader.format() {
|
||||
|
@ -308,7 +309,7 @@ impl ImageCache {
|
|||
let (w, h) = reader.into_dimensions().map_err(|e| e.to_string())?;
|
||||
Ok((fmt, PxSize::new(Px(w as i32), Px(h as i32))))
|
||||
}
|
||||
None => Err("unknown format".to_string()),
|
||||
None => Err(Txt::from_static("unknown format")),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -800,9 +801,9 @@ impl ImageCache {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn encode(&self, id: ImageId, format: String) {
|
||||
pub fn encode(&self, id: ImageId, format: Txt) {
|
||||
if !ENCODERS.contains(&format.as_str()) {
|
||||
let error = format!("cannot encode `{id:?}` to `{format}`, unknown format");
|
||||
let error = formatx!("cannot encode `{id:?}` to `{format}`, unknown format");
|
||||
let _ = self
|
||||
.app_sender
|
||||
.send(AppEvent::Notify(Event::ImageEncodeError { image: id, format, error }));
|
||||
|
@ -810,7 +811,7 @@ impl ImageCache {
|
|||
}
|
||||
|
||||
if let Some(img) = self.get(id) {
|
||||
let fmt = image::ImageFormat::from_extension(&format).unwrap();
|
||||
let fmt = image::ImageFormat::from_extension(format.as_str()).unwrap();
|
||||
debug_assert!(fmt.can_write());
|
||||
|
||||
let img = img.clone();
|
||||
|
@ -826,13 +827,13 @@ impl ImageCache {
|
|||
}));
|
||||
}
|
||||
Err(e) => {
|
||||
let error = format!("failed to encode `{id:?}` to `{format}`, {e}");
|
||||
let error = formatx!("failed to encode `{id:?}` to `{format}`, {e}");
|
||||
let _ = sender.send(AppEvent::Notify(Event::ImageEncodeError { image: id, format, error }));
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
let error = format!("cannot encode `{id:?}` to `{format}`, image not found");
|
||||
let error = formatx!("cannot encode `{id:?}` to `{format}`, image not found");
|
||||
let _ = self
|
||||
.app_sender
|
||||
.send(AppEvent::Notify(Event::ImageEncodeError { image: id, format, error }));
|
||||
|
@ -1197,6 +1198,7 @@ mod capture {
|
|||
api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat},
|
||||
Renderer,
|
||||
};
|
||||
use zero_ui_txt::formatx;
|
||||
use zero_ui_units::{Factor, PxRect};
|
||||
use zero_ui_view_api::{
|
||||
image::{ImageDataFormat, ImageId, ImageLoadedData, ImageMaskMode, ImagePpi, ImageRequest},
|
||||
|
@ -1230,7 +1232,7 @@ mod capture {
|
|||
let id = self.image_id_gen.incr();
|
||||
let _ = self.app_sender.send(AppEvent::Notify(Event::ImageLoadError {
|
||||
image: id,
|
||||
error: format!("no frame rendered in window `{window_id:?}`"),
|
||||
error: formatx!("no frame rendered in window `{window_id:?}`"),
|
||||
}));
|
||||
let _ = self.app_sender.send(AppEvent::Notify(Event::FrameImageReady {
|
||||
window: window_id,
|
||||
|
|
|
@ -139,6 +139,7 @@ pub use gleam;
|
|||
|
||||
use webrender::api::*;
|
||||
use window::Window;
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::{Dip, DipPoint, DipRect, DipSize, Factor, Px, PxPoint, PxRect, PxToDip};
|
||||
use zero_ui_view_api::{
|
||||
api_extension::{ApiExtensionId, ApiExtensionPayload},
|
||||
|
@ -940,7 +941,7 @@ impl App {
|
|||
state,
|
||||
key,
|
||||
key_modified,
|
||||
text: event.text.map(|s| s.as_str().to_owned()).unwrap_or_default(),
|
||||
text: event.text.map(|s| Txt::from_str(s.as_str())).unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1180,7 +1181,7 @@ impl App {
|
|||
state: KeyState::Released,
|
||||
key: key.clone(),
|
||||
key_modified: key.clone(),
|
||||
text: String::new(),
|
||||
text: Txt::from_str(""),
|
||||
});
|
||||
}
|
||||
if matches!(key, Key::Shift) && !m.shift_key() {
|
||||
|
@ -1192,7 +1193,7 @@ impl App {
|
|||
state: KeyState::Released,
|
||||
key: key.clone(),
|
||||
key_modified: key.clone(),
|
||||
text: String::new(),
|
||||
text: Txt::from_str(""),
|
||||
});
|
||||
}
|
||||
if matches!(key, Key::Alt | Key::AltGraph) && !m.alt_key() {
|
||||
|
@ -1204,7 +1205,7 @@ impl App {
|
|||
state: KeyState::Released,
|
||||
key: key.clone(),
|
||||
key_modified: key.clone(),
|
||||
text: String::new(),
|
||||
text: Txt::from_str(""),
|
||||
});
|
||||
}
|
||||
if matches!(key, Key::Ctrl) && !m.control_key() {
|
||||
|
@ -1216,7 +1217,7 @@ impl App {
|
|||
state: KeyState::Released,
|
||||
key: key.clone(),
|
||||
key_modified: key.clone(),
|
||||
text: String::new(),
|
||||
text: Txt::from_str(""),
|
||||
});
|
||||
}
|
||||
retain
|
||||
|
@ -1623,7 +1624,7 @@ impl Api for App {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_title(&mut self, id: WindowId, title: String) {
|
||||
fn set_title(&mut self, id: WindowId, title: Txt) {
|
||||
self.with_window(id, |w| w.set_title(title), || ())
|
||||
}
|
||||
|
||||
|
@ -1719,12 +1720,12 @@ impl Api for App {
|
|||
self.with_window(id, |w| w.set_ime_area(area), || ())
|
||||
}
|
||||
|
||||
fn image_decoders(&mut self) -> Vec<String> {
|
||||
image_cache::DECODERS.iter().map(|&s| s.to_owned()).collect()
|
||||
fn image_decoders(&mut self) -> Vec<Txt> {
|
||||
image_cache::DECODERS.iter().map(|&s| Txt::from_static(s)).collect()
|
||||
}
|
||||
|
||||
fn image_encoders(&mut self) -> Vec<String> {
|
||||
image_cache::ENCODERS.iter().map(|&s| s.to_owned()).collect()
|
||||
fn image_encoders(&mut self) -> Vec<Txt> {
|
||||
image_cache::ENCODERS.iter().map(|&s| Txt::from_static(s)).collect()
|
||||
}
|
||||
|
||||
fn add_image(&mut self, request: ImageRequest<IpcBytes>) -> ImageId {
|
||||
|
@ -1739,7 +1740,7 @@ impl Api for App {
|
|||
self.image_cache.forget(id)
|
||||
}
|
||||
|
||||
fn encode_image(&mut self, id: ImageId, format: String) {
|
||||
fn encode_image(&mut self, id: ImageId, format: Txt) {
|
||||
self.image_cache.encode(id, format)
|
||||
}
|
||||
|
||||
|
@ -1823,7 +1824,7 @@ impl Api for App {
|
|||
if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
|
||||
s.message_dialog(dialog, r_id, self.app_sender.clone());
|
||||
} else {
|
||||
let r = MsgDialogResponse::Error("window not found".to_owned());
|
||||
let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
|
||||
let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
|
||||
}
|
||||
r_id
|
||||
|
@ -1834,7 +1835,7 @@ impl Api for App {
|
|||
if let Some(s) = self.windows.iter_mut().find(|s| s.id() == id) {
|
||||
s.file_dialog(dialog, r_id, self.app_sender.clone());
|
||||
} else {
|
||||
let r = MsgDialogResponse::Error("window not found".to_owned());
|
||||
let r = MsgDialogResponse::Error(Txt::from_static("window not found"));
|
||||
let _ = self.app_sender.send(AppEvent::Notify(Event::MsgDialogResponse(r_id, r)));
|
||||
};
|
||||
r_id
|
||||
|
@ -1848,7 +1849,7 @@ impl Api for App {
|
|||
|
||||
clipboard_win::get(clipboard_win::formats::Unicode)
|
||||
.map_err(util::clipboard_win_to_clip)
|
||||
.map(clipboard::ClipboardData::Text)
|
||||
.map(|s: String| clipboard::ClipboardData::Text(Txt::from_str(&s)))
|
||||
}
|
||||
clipboard::ClipboardType::Image => {
|
||||
let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
|
||||
|
@ -1856,7 +1857,7 @@ impl Api for App {
|
|||
let bitmap = clipboard_win::get(clipboard_win::formats::Bitmap).map_err(util::clipboard_win_to_clip)?;
|
||||
|
||||
let id = self.image_cache.add(ImageRequest {
|
||||
format: image::ImageDataFormat::FileExtension("bmp".to_owned()),
|
||||
format: image::ImageDataFormat::FileExtension(Txt::from_str("bmp")),
|
||||
data: IpcBytes::from_vec(bitmap),
|
||||
max_decoded_len: u64::MAX,
|
||||
downscale: None,
|
||||
|
@ -1877,6 +1878,8 @@ impl Api for App {
|
|||
|
||||
#[cfg(windows)]
|
||||
fn write_clipboard(&mut self, data: clipboard::ClipboardData) -> Result<(), clipboard::ClipboardError> {
|
||||
use zero_ui_txt::formatx;
|
||||
|
||||
match data {
|
||||
clipboard::ClipboardData::Text(t) => {
|
||||
let _clip = clipboard_win::Clipboard::new_attempts(10).map_err(util::clipboard_win_to_clip)?;
|
||||
|
@ -1889,10 +1892,10 @@ impl Api for App {
|
|||
if let Some(img) = self.image_cache.get(id) {
|
||||
let mut bmp = vec![];
|
||||
img.encode(::image::ImageFormat::Bmp, &mut bmp)
|
||||
.map_err(|e| clipboard::ClipboardError::Other(format!("{e:?}")))?;
|
||||
.map_err(|e| clipboard::ClipboardError::Other(formatx!("{e:?}")))?;
|
||||
clipboard_win::set(clipboard_win::formats::Bitmap, bmp).map_err(util::clipboard_win_to_clip)
|
||||
} else {
|
||||
Err(clipboard::ClipboardError::Other("image not found".to_owned()))
|
||||
Err(clipboard::ClipboardError::Other(Txt::from_str("image not found")))
|
||||
}
|
||||
}
|
||||
clipboard::ClipboardData::FileList(l) => {
|
||||
|
|
|
@ -2,6 +2,7 @@ use std::{cell::Cell, sync::Arc};
|
|||
|
||||
use rayon::ThreadPoolBuilder;
|
||||
use winit::{event::ElementState, monitor::MonitorHandle};
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::*;
|
||||
use zero_ui_view_api::access::AccessNodeId;
|
||||
use zero_ui_view_api::clipboard as clipboard_api;
|
||||
|
@ -251,7 +252,7 @@ pub(crate) fn monitor_handle_to_info(handle: &MonitorHandle) -> MonitorInfo {
|
|||
let position = handle.position().to_px();
|
||||
let size = handle.size().to_px();
|
||||
MonitorInfo {
|
||||
name: handle.name().unwrap_or_default(),
|
||||
name: Txt::from_str(&handle.name().unwrap_or_default()),
|
||||
position,
|
||||
size,
|
||||
scale_factor: Factor(handle.scale_factor() as _),
|
||||
|
@ -1035,13 +1036,15 @@ pub(crate) fn arboard_to_clip(e: arboard::Error) -> clipboard_api::ClipboardErro
|
|||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn clipboard_win_to_clip(e: clipboard_win::SystemError) -> clipboard_api::ClipboardError {
|
||||
use zero_ui_txt::formatx;
|
||||
|
||||
if e == clipboard_win::SystemError::unimplemented() {
|
||||
clipboard_api::ClipboardError::NotSupported
|
||||
} else if e.is_zero() {
|
||||
// If GetClipboardData fails it returns a NULL, but GetLastError sometimes (always?) returns 0 (ERROR_SUCCESS)
|
||||
clipboard_api::ClipboardError::NotFound
|
||||
} else {
|
||||
clipboard_api::ClipboardError::Other(format!("{e:?}"))
|
||||
clipboard_api::ClipboardError::Other(formatx!("{e:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1080,9 +1083,9 @@ pub(crate) fn accesskit_to_event(
|
|||
Action::ShowTooltip => AccessCmd::SetToolTipVis(true),
|
||||
Action::ReplaceSelectedText => {
|
||||
if let Some(accesskit::ActionData::Value(s)) = request.data {
|
||||
AccessCmd::ReplaceSelectedText(s.to_string())
|
||||
AccessCmd::ReplaceSelectedText(Txt::from_str(&s))
|
||||
} else {
|
||||
AccessCmd::ReplaceSelectedText(String::new())
|
||||
AccessCmd::ReplaceSelectedText(Txt::from_str(""))
|
||||
}
|
||||
}
|
||||
Action::ScrollBackward => AccessCmd::Scroll(ScrollCmd::PageUp),
|
||||
|
@ -1128,7 +1131,7 @@ pub(crate) fn accesskit_to_event(
|
|||
}
|
||||
}
|
||||
Action::SetValue => match request.data {
|
||||
Some(accesskit::ActionData::Value(s)) => AccessCmd::SetString(s.to_string()),
|
||||
Some(accesskit::ActionData::Value(s)) => AccessCmd::SetString(Txt::from_str(&s)),
|
||||
Some(accesskit::ActionData::NumericValue(n)) => AccessCmd::SetNumber(n),
|
||||
_ => return None,
|
||||
},
|
||||
|
@ -1284,7 +1287,7 @@ fn access_node_to_kit(
|
|||
builder.set_invalid(accesskit::Invalid::True)
|
||||
}
|
||||
}
|
||||
Label(s) => builder.set_name(s.clone().into_boxed_str()),
|
||||
Label(s) => builder.set_name(s.clone().into_owned().into_boxed_str()),
|
||||
Level(n) => builder.set_hierarchical_level(n.get() as usize),
|
||||
Modal => builder.set_modal(),
|
||||
MultiSelectable => builder.set_multiselectable(),
|
||||
|
@ -1292,7 +1295,7 @@ fn access_node_to_kit(
|
|||
access::Orientation::Horizontal => builder.set_orientation(accesskit::Orientation::Horizontal),
|
||||
access::Orientation::Vertical => builder.set_orientation(accesskit::Orientation::Vertical),
|
||||
},
|
||||
Placeholder(p) => builder.set_placeholder(p.clone().into_boxed_str()),
|
||||
Placeholder(p) => builder.set_placeholder(p.clone().into_owned().into_boxed_str()),
|
||||
ReadOnly => builder.set_read_only(),
|
||||
Required => builder.set_required(),
|
||||
Selected => builder.set_selected(true),
|
||||
|
@ -1303,7 +1306,7 @@ fn access_node_to_kit(
|
|||
ValueMax(m) => builder.set_max_numeric_value(*m),
|
||||
ValueMin(m) => builder.set_min_numeric_value(*m),
|
||||
Value(v) => builder.set_numeric_value(*v),
|
||||
ValueText(v) => builder.set_value(v.clone().into_boxed_str()),
|
||||
ValueText(v) => builder.set_value(v.clone().into_owned().into_boxed_str()),
|
||||
Live { indicator, atomic, busy } => {
|
||||
builder.set_live(match indicator {
|
||||
access::LiveIndicator::Assertive => accesskit::Live::Assertive,
|
||||
|
|
|
@ -24,6 +24,7 @@ use winit::{
|
|||
monitor::{MonitorHandle, VideoMode as GVideoMode},
|
||||
window::{Fullscreen, Icon, Window as GWindow, WindowBuilder},
|
||||
};
|
||||
use zero_ui_txt::Txt;
|
||||
use zero_ui_units::{Dip, DipPoint, DipRect, DipSize, DipToPx, Factor, Px, PxPoint, PxRect, PxToDip, PxVector};
|
||||
use zero_ui_view_api::{
|
||||
api_extension::{ApiExtensionId, ApiExtensionPayload},
|
||||
|
@ -263,7 +264,7 @@ impl Window {
|
|||
state: KeyState::Pressed,
|
||||
key: Key::F4,
|
||||
key_modified: Key::F4,
|
||||
text: String::new(),
|
||||
text: Txt::from_static(""),
|
||||
}));
|
||||
return Some(0);
|
||||
}
|
||||
|
@ -470,7 +471,7 @@ impl Window {
|
|||
self.rendered_frame_id
|
||||
}
|
||||
|
||||
pub fn set_title(&self, title: String) {
|
||||
pub fn set_title(&self, title: Txt) {
|
||||
self.window.set_title(&title);
|
||||
}
|
||||
|
||||
|
@ -1533,7 +1534,7 @@ impl Window {
|
|||
if already_open {
|
||||
let _ = event_sender.send(AppEvent::Notify(Event::MsgDialogResponse(
|
||||
id,
|
||||
dlg_api::MsgDialogResponse::Error("dialog already open".to_owned()),
|
||||
dlg_api::MsgDialogResponse::Error(Txt::from_static("dialog already open")),
|
||||
)));
|
||||
}
|
||||
already_open
|
||||
|
@ -1556,8 +1557,8 @@ impl Window {
|
|||
dlg_api::MsgDialogButtons::OkCancel => rfd::MessageButtons::OkCancel,
|
||||
dlg_api::MsgDialogButtons::YesNo => rfd::MessageButtons::YesNo,
|
||||
})
|
||||
.set_title(&dialog.title)
|
||||
.set_description(&dialog.message)
|
||||
.set_title(dialog.title.as_str())
|
||||
.set_description(dialog.message.as_str())
|
||||
.set_parent(&self.window);
|
||||
|
||||
let modal_dialog_active = self.modal_dialog_active.clone();
|
||||
|
@ -1593,9 +1594,9 @@ impl Window {
|
|||
}
|
||||
|
||||
let mut dlg = rfd::AsyncFileDialog::new()
|
||||
.set_title(&dialog.title)
|
||||
.set_title(dialog.title.as_str())
|
||||
.set_directory(&dialog.starting_dir)
|
||||
.set_file_name(&dialog.starting_name)
|
||||
.set_file_name(dialog.starting_name.as_str())
|
||||
.set_parent(&self.window);
|
||||
for (name, patterns) in dialog.iter_filters() {
|
||||
dlg = dlg.add_filter(name, &patterns.map(|s| s.trim_start_matches(['*', '.'])).collect::<Vec<_>>());
|
||||
|
|
|
@ -42,7 +42,7 @@ fn on_build(wgt: &mut WidgetBuilding) {
|
|||
wgt.set_child(node);
|
||||
|
||||
let source = wgt.capture_var::<ImageSource>(property_id!(source)).unwrap_or_else(|| {
|
||||
let error = Img::dummy(Some("no source".to_owned()));
|
||||
let error = Img::dummy(Some(Txt::from_static("no source")));
|
||||
let error = ImageSource::Image(var(error).read_only());
|
||||
LocalVar(error).boxed()
|
||||
});
|
||||
|
@ -60,7 +60,7 @@ mod tests {
|
|||
fn error_view_recursion() {
|
||||
crate::core::test_log();
|
||||
|
||||
let img = var(crate::core::image::Img::dummy(Some("test error".to_string()))).read_only();
|
||||
let img = var(crate::core::image::Img::dummy(Some(Txt::from_static("test error")))).read_only();
|
||||
|
||||
let mut app = App::default().run_headless(false);
|
||||
IMAGES.load_in_headless().set(true);
|
||||
|
|
|
@ -18,7 +18,7 @@ context_var! {
|
|||
pub static CONTEXT_IMAGE_VAR: Img = no_context_image();
|
||||
}
|
||||
fn no_context_image() -> Img {
|
||||
Img::dummy(Some("no image source in context".to_owned()))
|
||||
Img::dummy(Some(Txt::from_static("no image source in context")))
|
||||
}
|
||||
|
||||
/// Requests an image from [`IMAGES`] and sets [`CONTEXT_IMAGE_VAR`].
|
||||
|
|
Loading…
Reference in New Issue