pub struct ValidationMessage {
pub severity: ValidationSeverity,
pub message: String,
}Fields§
§severity: ValidationSeverity§message: StringImplementations§
Source§impl ValidationMessage
impl ValidationMessage
Sourcepub fn error(message: impl Into<String>) -> Self
pub fn error(message: impl Into<String>) -> Self
Examples found in repository?
examples/showcase.rs (line 4707)
4652fn form_widgets(ui: &mut UiDocument, parent: UiNodeId, state: &ShowcaseState) {
4653 let body = section(ui, parent, "forms", "Forms");
4654 let section = widgets::form_section(
4655 ui,
4656 body,
4657 "forms.profile",
4658 Some("Profile".to_string()),
4659 widgets::FormSectionOptions::default().with_layout(
4660 LayoutStyle::column()
4661 .with_width_percent(1.0)
4662 .with_padding(12.0)
4663 .with_gap(10.0),
4664 ),
4665 );
4666 let name = widgets::form_row(
4667 ui,
4668 section.root,
4669 "forms.profile.name",
4670 widgets::FormRowOptions::default(),
4671 );
4672 widgets::field_label(
4673 ui,
4674 name,
4675 "forms.profile.name.label",
4676 "Name",
4677 widgets::FieldLabelOptions::default().required(),
4678 );
4679 widgets::field_help_text(
4680 ui,
4681 name,
4682 "forms.profile.name.help",
4683 "Shown in window titles and project lists.",
4684 widgets::FieldHelpOptions::default(),
4685 );
4686 form_text_field(ui, name, "forms.profile.name.input", "Ada Lovelace");
4687
4688 let email = widgets::form_row(
4689 ui,
4690 section.root,
4691 "forms.profile.email",
4692 widgets::FormRowOptions::default()
4693 .required()
4694 .invalid("Use a complete email address"),
4695 );
4696 widgets::field_label(
4697 ui,
4698 email,
4699 "forms.profile.email.label",
4700 "Email",
4701 widgets::FieldLabelOptions::default().required(),
4702 );
4703 widgets::field_validation_message(
4704 ui,
4705 email,
4706 "forms.profile.email.validation",
4707 ValidationMessage::error("Use a complete email address"),
4708 widgets::ValidationMessageOptions::default(),
4709 );
4710 form_text_field(ui, email, "forms.profile.email.input", "ada@");
4711
4712 let role = widgets::form_row(
4713 ui,
4714 section.root,
4715 "forms.profile.role",
4716 widgets::FormRowOptions::default(),
4717 );
4718 widgets::field_label(
4719 ui,
4720 role,
4721 "forms.profile.role.label",
4722 "Role",
4723 widgets::FieldLabelOptions::default(),
4724 );
4725 widgets::field_help_text(
4726 ui,
4727 role,
4728 "forms.profile.role.help",
4729 "Form rows compose labels, controls, help, and validation text.",
4730 widgets::FieldHelpOptions::default(),
4731 );
4732 form_text_field(ui, role, "forms.profile.role.input", "Maintainer");
4733
4734 widgets::form_error_summary(
4735 ui,
4736 section.root,
4737 "forms.profile.errors",
4738 &state.form,
4739 widgets::FormErrorSummaryOptions::default(),
4740 );
4741 widgets::form_action_buttons(
4742 ui,
4743 section.root,
4744 "forms.profile.actions",
4745 &state.form,
4746 widgets::FormActionButtonsOptions::default()
4747 .include_reset(true)
4748 .with_action_prefix("forms.profile"),
4749 );
4750 widgets::label(
4751 ui,
4752 section.root,
4753 "forms.profile.status",
4754 format!("Status: {}", state.form_status),
4755 text(11.0, color(154, 166, 184)),
4756 LayoutStyle::new().with_width_percent(1.0),
4757 );
4758}
4759
4760fn overlay_widgets(ui: &mut UiDocument, parent: UiNodeId, state: &ShowcaseState) {
4761 let body = section(ui, parent, "overlays", "Overlays");
4762 let header = widgets::collapsing_header(
4763 ui,
4764 body,
4765 "overlays.collapsing",
4766 "Collapsing header",
4767 widgets::CollapsingHeaderOptions::default()
4768 .expanded(state.overlay_expanded)
4769 .with_toggle_action("overlays.collapsing.toggle"),
4770 );
4771 if let Some(panel) = header.body {
4772 widgets::label(
4773 ui,
4774 panel,
4775 "overlays.collapsing.body",
4776 "Expanded content lives under the header.",
4777 text(12.0, color(196, 210, 230)),
4778 LayoutStyle::new().with_width_percent(1.0),
4779 );
4780 }
4781
4782 let controls = row(ui, body, "overlays.controls", 8.0);
4783 button(
4784 ui,
4785 controls,
4786 "overlays.popup.toggle",
4787 if state.overlay_popup_open {
4788 "Close popup"
4789 } else {
4790 "Open popup"
4791 },
4792 "overlays.popup.toggle",
4793 button_visual(48, 112, 184),
4794 );
4795 button(
4796 ui,
4797 controls,
4798 "overlays.modal.open",
4799 "Open modal",
4800 "overlays.modal.open",
4801 button_visual(58, 78, 96),
4802 );
4803
4804 let tooltip = TooltipContent::new("Tooltip")
4805 .body("Tooltip boxes are overlay surfaces with title, body, and shortcut text.")
4806 .shortcut_label("Ctrl+K");
4807 let mut tooltip_options = widgets::TooltipBoxOptions::default()
4808 .with_layout(
4809 LayoutStyle::column()
4810 .with_width(280.0)
4811 .with_padding(8.0)
4812 .with_gap(4.0),
4813 )
4814 .with_animation(None);
4815 tooltip_options.layer = UiLayer::AppContent;
4816 tooltip_options.z_index = 0;
4817 widgets::tooltip_box(ui, body, "overlays.tooltip", tooltip, tooltip_options);
4818
4819 if state.overlay_popup_open {
4820 let popup = widgets::popup_panel(
4821 ui,
4822 parent,
4823 "overlays.popup_panel",
4824 UiRect::new(0.0, 20.0, 160.0, 96.0),
4825 widgets::PopupOptions {
4826 z_index: 20,
4827 accessibility: Some(
4828 AccessibilityMeta::new(AccessibilityRole::Dialog).label("Popup"),
4829 ),
4830 ..Default::default()
4831 },
4832 );
4833 let popup_body = ui.add_child(
4834 popup,
4835 UiNode::container(
4836 "overlays.popup_panel.body",
4837 LayoutStyle::column()
4838 .with_width_percent(1.0)
4839 .with_height_percent(1.0)
4840 .with_padding(10.0)
4841 .with_gap(6.0),
4842 ),
4843 );
4844 let popup_header = row(ui, popup_body, "overlays.popup_panel.header", 8.0);
4845 widgets::label(
4846 ui,
4847 popup_header,
4848 "overlays.popup_panel.label",
4849 "Popup panel",
4850 text(12.0, color(220, 228, 238)),
4851 LayoutStyle::new().with_width_percent(1.0),
4852 );
4853 let mut close = widgets::ButtonOptions::new(LayoutStyle::size(26.0, 22.0))
4854 .with_action("overlays.popup.close");
4855 close.visual = UiVisual::panel(color(28, 34, 43), None, 3.0);
4856 close.hovered_visual = Some(button_visual(54, 70, 92));
4857 close.text_style = text(12.0, color(220, 228, 238));
4858 widgets::button(ui, popup_header, "overlays.popup_panel.close", "x", close);
4859 widgets::label(
4860 ui,
4861 popup_body,
4862 "overlays.popup_panel.body_text",
4863 "Popup content is conditionally rendered.",
4864 text(11.0, color(196, 210, 230)),
4865 LayoutStyle::new().with_width_percent(1.0),
4866 );
4867 }
4868
4869 if state.overlay_modal_open {
4870 let modal = widgets::modal_dialog(
4871 ui,
4872 body,
4873 "overlays.modal",
4874 "Modal dialog",
4875 widgets::ModalDialogOptions::default()
4876 .with_size(320.0, 180.0)
4877 .with_close_action("overlays.modal.close")
4878 .with_dismissal(widgets::DialogDismissal::STANDARD)
4879 .with_focus_restore(FocusRestoreTarget::Previous)
4880 .modeless(),
4881 );
4882 widgets::label(
4883 ui,
4884 modal.body,
4885 "overlays.modal.body.text",
4886 "Dialog body",
4887 text(12.0, color(220, 228, 238)),
4888 LayoutStyle::new().with_width_percent(1.0),
4889 );
4890 }
4891}
4892
4893fn drag_drop_widgets(ui: &mut UiDocument, parent: UiNodeId, state: &ShowcaseState) {
4894 let body = section(ui, parent, "drag_drop", "Drag and drop");
4895 widgets::dnd_drag_source(
4896 ui,
4897 body,
4898 "drag_drop.text_source",
4899 "Drag text payload",
4900 DragPayload::text("Operad payload"),
4901 widgets::DragSourceOptions::default()
4902 .with_kind(DragDropSurfaceKind::ListRow)
4903 .with_action("drag_drop.text_source")
4904 .with_accessibility_hint("Start a text drag operation"),
4905 );
4906
4907 let accepted_options = widgets::DropZoneOptions::default()
4908 .with_kind(DragDropSurfaceKind::EditorSurface)
4909 .with_accepted_payload(DropPayloadFilter::empty().text())
4910 .with_action("drag_drop.accept_text")
4911 .with_accessibility_hint("Accepts text payloads");
4912 let accepted = widgets::dnd_drop_zone(
4913 ui,
4914 body,
4915 "drag_drop.accept_text",
4916 "Drop text here",
4917 accepted_options.clone(),
4918 );
4919 widgets::dnd_apply_drop_zone_preview(
4920 ui,
4921 accepted.root,
4922 &accepted_options,
4923 widgets::DropZonePreviewState::Accepted,
4924 );
4925
4926 let rejected_options = widgets::DropZoneOptions::default()
4927 .with_layout(
4928 LayoutStyle::column()
4929 .with_width(240.0)
4930 .with_height(82.0)
4931 .with_padding(12.0),
4932 )
4933 .with_kind(DragDropSurfaceKind::Asset)
4934 .with_accepted_payload(DropPayloadFilter::empty().files())
4935 .with_action("drag_drop.files_only");
4936 let rejected = widgets::dnd_drop_zone(
4937 ui,
4938 body,
4939 "drag_drop.files_only",
4940 "Files only",
4941 rejected_options.clone(),
4942 );
4943 widgets::dnd_apply_drop_zone_preview(
4944 ui,
4945 rejected.root,
4946 &rejected_options,
4947 widgets::DropZonePreviewState::Rejected,
4948 );
4949 widgets::label(
4950 ui,
4951 body,
4952 "drag_drop.status",
4953 format!("Status: {}", state.drag_drop_status),
4954 text(11.0, color(154, 166, 184)),
4955 LayoutStyle::new().with_width_percent(1.0),
4956 );
4957}
4958
4959fn media_widgets(ui: &mut UiDocument, parent: UiNodeId) {
4960 let body = section(ui, parent, "media", "Media");
4961 let icons = row(ui, body, "media.icons", 10.0);
4962 widgets::image(
4963 ui,
4964 icons,
4965 "media.image.play",
4966 icon_image(BuiltInIcon::Play),
4967 widgets::ImageOptions::default()
4968 .with_layout(LayoutStyle::size(42.0, 42.0))
4969 .with_accessibility_label("Play icon"),
4970 );
4971 widgets::image(
4972 ui,
4973 icons,
4974 "media.image.warning",
4975 ImageContent::new(BuiltInIcon::Warning.key()).tinted(color(232, 186, 88)),
4976 widgets::ImageOptions::default()
4977 .with_layout(LayoutStyle::size(42.0, 42.0))
4978 .with_accessibility_label("Warning icon"),
4979 );
4980 widgets::image(
4981 ui,
4982 icons,
4983 "media.image.info",
4984 ImageContent::new(BuiltInIcon::Info.key()).tinted(color(118, 183, 255)),
4985 widgets::ImageOptions::default()
4986 .with_layout(LayoutStyle::size(42.0, 42.0))
4987 .with_accessibility_label("Info icon"),
4988 );
4989 widgets::label(
4990 ui,
4991 body,
4992 "media.image.note",
4993 "Image widgets reference stable resource keys; the host resolves them to textures or vector assets.",
4994 text(12.0, color(166, 176, 190)),
4995 LayoutStyle::new().with_width_percent(1.0),
4996 );
4997}
4998
4999fn timeline_ruler(ui: &mut UiDocument, parent: UiNodeId) {
5000 let mut layout = LayoutStyle::column()
5001 .with_width_percent(1.0)
5002 .with_height(40.0)
5003 .with_flex_shrink(0.0);
5004 layout.as_taffy_style_mut().min_size.width = operad::length(0.0);
5005 layout.as_taffy_style_mut().min_size.height = operad::length(0.0);
5006 let body = widgets::scroll_area(ui, parent, "timeline", ScrollAxes::BOTH, layout);
5007 widgets::timeline_ruler(
5008 ui,
5009 body,
5010 "timeline.ruler",
5011 widgets::RulerSpec {
5012 range: widgets::TimelineRange::new(0.0, 12.0),
5013 width: 600.0,
5014 major_step: 2.0,
5015 minor_step: 0.5,
5016 label_every: 1,
5017 },
5018 widgets::TimelineRulerOptions::default(),
5019 );
5020}
5021
5022fn toast_controls(ui: &mut UiDocument, parent: UiNodeId, state: &ShowcaseState) {
5023 let body = section(ui, parent, "toasts", "Toasts");
5024 let controls = row(ui, body, "toasts.controls", 10.0);
5025 button(
5026 ui,
5027 controls,
5028 "toasts.show",
5029 "Show toast",
5030 "toast.show",
5031 button_visual(48, 112, 184),
5032 );
5033 button(
5034 ui,
5035 controls,
5036 "toasts.hide",
5037 "Hide",
5038 "toast.hide",
5039 button_visual(58, 78, 96),
5040 );
5041 widgets::label(
5042 ui,
5043 body,
5044 "toasts.status",
5045 if state.toast_visible {
5046 "Toast overlay is visible."
5047 } else {
5048 "Toast overlay is hidden."
5049 },
5050 text(12.0, color(196, 210, 230)),
5051 LayoutStyle::new().with_width_percent(1.0),
5052 );
5053 widgets::label(
5054 ui,
5055 body,
5056 "toasts.action_status",
5057 format!("Action: {}", state.toast_action_status),
5058 text(12.0, color(154, 166, 184)),
5059 LayoutStyle::new().with_width_percent(1.0),
5060 );
5061}
5062
5063fn popup_controls(ui: &mut UiDocument, parent: UiNodeId, state: &ShowcaseState) {
5064 let body = section(ui, parent, "popup_panel", "Popup panel");
5065 let controls = row(ui, body, "popup_panel.controls", 8.0);
5066 button(
5067 ui,
5068 controls,
5069 "popup_panel.toggle",
5070 if state.popup_open {
5071 "Close popup"
5072 } else {
5073 "Open popup"
5074 },
5075 "popup.toggle",
5076 button_visual(48, 112, 184),
5077 );
5078 if state.popup_open {
5079 let mut close =
5080 widgets::ButtonOptions::new(LayoutStyle::size(30.0, 30.0)).with_action("popup.close");
5081 close.visual = UiVisual::panel(color(28, 34, 43), None, 3.0);
5082 close.hovered_visual = Some(button_visual(54, 70, 92));
5083 close.text_style = text(13.0, color(220, 228, 238));
5084 widgets::button(ui, controls, "popup_panel.inline_close", "x", close);
5085 }
5086 widgets::label(
5087 ui,
5088 body,
5089 "popup_panel.status",
5090 if state.popup_open {
5091 "Popup overlay is open."
5092 } else {
5093 "Popup overlay is closed."
5094 },
5095 text(12.0, color(196, 210, 230)),
5096 LayoutStyle::new().with_width_percent(1.0),
5097 );
5098 if state.popup_open {
5099 let panel = widgets::popup_panel(
5100 ui,
5101 parent,
5102 "popup_panel.inline_preview",
5103 UiRect::new(0.0, 20.0, 160.0, 104.0),
5104 widgets::PopupOptions {
5105 z_index: 4,
5106 accessibility: Some(
5107 AccessibilityMeta::new(AccessibilityRole::Dialog).label("Popup preview"),
5108 ),
5109 ..Default::default()
5110 },
5111 );
5112 let content = ui.add_child(
5113 panel,
5114 UiNode::container(
5115 "popup_panel.inline_preview.body",
5116 LayoutStyle::column()
5117 .with_width_percent(1.0)
5118 .with_height_percent(1.0)
5119 .with_padding(10.0)
5120 .with_gap(8.0),
5121 ),
5122 );
5123 let header = row(ui, content, "popup_panel.inline_preview.header", 8.0);
5124 widgets::label(
5125 ui,
5126 header,
5127 "popup_panel.inline_preview.title",
5128 "Popup panel",
5129 text(12.0, color(226, 234, 246)),
5130 LayoutStyle::new().with_width_percent(1.0),
5131 );
5132 let mut close =
5133 widgets::ButtonOptions::new(LayoutStyle::size(26.0, 22.0)).with_action("popup.close");
5134 close.visual = UiVisual::panel(color(28, 34, 43), None, 3.0);
5135 close.hovered_visual = Some(button_visual(54, 70, 92));
5136 close.text_style = text(12.0, color(220, 228, 238));
5137 widgets::button(ui, header, "popup_panel.inline_preview.close", "x", close);
5138 widgets::label(
5139 ui,
5140 content,
5141 "popup_panel.inline_preview.text",
5142 "Overlay content",
5143 text(11.0, color(196, 210, 230)),
5144 LayoutStyle::new().with_width_percent(1.0),
5145 );
5146 widgets::spacer(
5147 ui,
5148 body,
5149 "popup_panel.inline_preview.space",
5150 LayoutStyle::new()
5151 .with_width_percent(1.0)
5152 .with_height(112.0)
5153 .with_flex_shrink(0.0),
5154 );
5155 }
5156}
5157
5158fn styling_widgets(ui: &mut UiDocument, parent: UiNodeId, state: &ShowcaseState) {
5159 let body = section(ui, parent, "styling", "Styling");
5160 let grid = ui.add_child(
5161 body,
5162 UiNode::container(
5163 "styling.grid",
5164 LayoutStyle::row()
5165 .with_width_percent(1.0)
5166 .with_height_percent(1.0)
5167 .gap(16.0),
5168 ),
5169 );
5170 let controls = ui.add_child(
5171 grid,
5172 UiNode::container(
5173 "styling.controls",
5174 LayoutStyle::column()
5175 .with_width(330.0)
5176 .with_height_percent(1.0)
5177 .with_flex_shrink(0.0)
5178 .gap(6.0),
5179 ),
5180 );
5181 style_checkbox(
5182 ui,
5183 controls,
5184 "styling.inner_same",
5185 "Inner margin same",
5186 state.styling.inner_same,
5187 );
5188 style_slider(
5189 ui,
5190 controls,
5191 "styling.inner",
5192 "Inner left",
5193 state.styling.inner_margin,
5194 0.0..32.0,
5195 );
5196 if !state.styling.inner_same {
5197 style_slider(
5198 ui,
5199 controls,
5200 "styling.inner_right",
5201 "Inner right",
5202 state.styling.inner_right,
5203 0.0..32.0,
5204 );
5205 style_slider(
5206 ui,
5207 controls,
5208 "styling.inner_top",
5209 "Inner top",
5210 state.styling.inner_top,
5211 0.0..32.0,
5212 );
5213 style_slider(
5214 ui,
5215 controls,
5216 "styling.inner_bottom",
5217 "Inner bottom",
5218 state.styling.inner_bottom,
5219 0.0..32.0,
5220 );
5221 }
5222 style_checkbox(
5223 ui,
5224 controls,
5225 "styling.outer_same",
5226 "Outer margin same",
5227 state.styling.outer_same,
5228 );
5229 style_slider(
5230 ui,
5231 controls,
5232 "styling.outer",
5233 "Outer left",
5234 state.styling.outer_margin,
5235 0.0..40.0,
5236 );
5237 if !state.styling.outer_same {
5238 style_slider(
5239 ui,
5240 controls,
5241 "styling.outer_right",
5242 "Outer right",
5243 state.styling.outer_right,
5244 0.0..40.0,
5245 );
5246 style_slider(
5247 ui,
5248 controls,
5249 "styling.outer_top",
5250 "Outer top",
5251 state.styling.outer_top,
5252 0.0..40.0,
5253 );
5254 style_slider(
5255 ui,
5256 controls,
5257 "styling.outer_bottom",
5258 "Outer bottom",
5259 state.styling.outer_bottom,
5260 0.0..40.0,
5261 );
5262 }
5263 style_checkbox(
5264 ui,
5265 controls,
5266 "styling.radius_same",
5267 "Corner radius same",
5268 state.styling.radius_same,
5269 );
5270 style_slider(
5271 ui,
5272 controls,
5273 "styling.radius",
5274 "Radius NW",
5275 state.styling.corner_radius,
5276 0.0..28.0,
5277 );
5278 if !state.styling.radius_same {
5279 style_slider(
5280 ui,
5281 controls,
5282 "styling.radius_ne",
5283 "Radius NE",
5284 state.styling.corner_ne,
5285 0.0..28.0,
5286 );
5287 style_slider(
5288 ui,
5289 controls,
5290 "styling.radius_sw",
5291 "Radius SW",
5292 state.styling.corner_sw,
5293 0.0..28.0,
5294 );
5295 style_slider(
5296 ui,
5297 controls,
5298 "styling.radius_se",
5299 "Radius SE",
5300 state.styling.corner_se,
5301 0.0..28.0,
5302 );
5303 }
5304 style_slider(
5305 ui,
5306 controls,
5307 "styling.shadow_x",
5308 "Shadow x",
5309 state.styling.shadow_x,
5310 -24.0..24.0,
5311 );
5312 style_slider(
5313 ui,
5314 controls,
5315 "styling.shadow_y",
5316 "Shadow y",
5317 state.styling.shadow_y,
5318 -24.0..24.0,
5319 );
5320 style_slider(
5321 ui,
5322 controls,
5323 "styling.shadow",
5324 "Shadow blur",
5325 state.styling.shadow_blur,
5326 0.0..32.0,
5327 );
5328 style_slider(
5329 ui,
5330 controls,
5331 "styling.shadow_spread",
5332 "Shadow spread",
5333 state.styling.shadow_spread,
5334 0.0..16.0,
5335 );
5336 style_slider(
5337 ui,
5338 controls,
5339 "styling.shadow_alpha",
5340 "Shadow color",
5341 state.styling.shadow_alpha,
5342 0.0..220.0,
5343 );
5344 style_slider(
5345 ui,
5346 controls,
5347 "styling.fill",
5348 "Fill color",
5349 state.styling.fill_tint,
5350 0.0..1.0,
5351 );
5352 style_slider(
5353 ui,
5354 controls,
5355 "styling.stroke_color",
5356 "Stroke color",
5357 state.styling.stroke_tint,
5358 0.0..1.0,
5359 );
5360 style_slider(
5361 ui,
5362 controls,
5363 "styling.stroke",
5364 "Stroke",
5365 state.styling.stroke_width,
5366 0.0..4.0,
5367 );
5368
5369 let preview = ui.add_child(
5370 grid,
5371 UiNode::container(
5372 "styling.preview",
5373 LayoutStyle::column()
5374 .with_width_percent(1.0)
5375 .with_height_percent(1.0)
5376 .padding(8.0),
5377 )
5378 .with_visual(UiVisual::panel(
5379 color(17, 20, 25),
5380 Some(StrokeStyle::new(color(56, 66, 82), 1.0)),
5381 4.0,
5382 )),
5383 );
5384 style_preview(ui, preview, state.styling);
5385}
5386
5387fn style_slider(
5388 ui: &mut UiDocument,
5389 parent: UiNodeId,
5390 name: &'static str,
5391 label: &'static str,
5392 value: f32,
5393 range: std::ops::Range<f32>,
5394) {
5395 let row = row(ui, parent, format!("{name}.row"), 8.0);
5396 widgets::label(
5397 ui,
5398 row,
5399 format!("{name}.label"),
5400 label,
5401 text(12.0, color(166, 176, 190)),
5402 LayoutStyle::new().with_width(118.0),
5403 );
5404 widgets::label(
5405 ui,
5406 row,
5407 format!("{name}.value"),
5408 if range.end <= 1.0 {
5409 format!("{value:.2}")
5410 } else {
5411 format!("{value:.0}")
5412 },
5413 text(12.0, color(226, 232, 242)),
5414 LayoutStyle::new().with_width(42.0),
5415 );
5416 let mut options = widgets::SliderOptions::default()
5417 .with_layout(
5418 LayoutStyle::new()
5419 .with_width(112.0)
5420 .with_height(20.0)
5421 .with_flex_shrink(0.0),
5422 )
5423 .with_value_edit_action(name);
5424 options.fill_color = color(120, 170, 230);
5425 widgets::slider(
5426 ui,
5427 row,
5428 format!("{name}.slider"),
5429 ((value - range.start) / (range.end - range.start).max(f32::EPSILON)).clamp(0.0, 1.0),
5430 0.0..1.0,
5431 options,
5432 );
5433}
5434
5435fn style_checkbox(
5436 ui: &mut UiDocument,
5437 parent: UiNodeId,
5438 name: &'static str,
5439 label: &'static str,
5440 checked: bool,
5441) {
5442 let mut options = widgets::CheckboxOptions::default().with_action(name);
5443 options.layout = LayoutStyle::new().with_width_percent(1.0).with_height(22.0);
5444 options.text_style = text(12.0, color(220, 228, 238));
5445 widgets::checkbox(ui, parent, name, label, checked, options);
5446}
5447
5448fn style_preview(ui: &mut UiDocument, parent: UiNodeId, styling: StylingState) {
5449 let outer = styling.outer_edges();
5450 let inner = styling.inner_edges();
5451 let frame = UiRect::new(
5452 22.0 + outer[0],
5453 28.0 + outer[2],
5454 108.0 + inner[0] + inner[1],
5455 40.0 + inner[2] + inner[3],
5456 );
5457 let text_rect = UiRect::new(
5458 frame.x + inner[0],
5459 frame.y + inner[2],
5460 (frame.width - inner[0] - inner[1]).max(1.0),
5461 (frame.height - inner[2] - inner[3]).max(1.0),
5462 );
5463 ui.add_child(
5464 parent,
5465 UiNode::scene(
5466 "styling.preview.scene",
5467 vec![
5468 ScenePrimitive::Rect(
5469 PaintRect::solid(frame, styling.fill_color())
5470 .stroke(AlignedStroke::inside(StrokeStyle::new(
5471 styling.stroke_color(),
5472 styling.stroke_width,
5473 )))
5474 .corner_radii(styling.radii())
5475 .effect(PaintEffect::shadow(
5476 styling.shadow_color(),
5477 UiPoint::new(styling.shadow_x, styling.shadow_y),
5478 styling.shadow_blur,
5479 styling.shadow_spread,
5480 )),
5481 ),
5482 ScenePrimitive::Text(
5483 PaintText::new("Content", text_rect, text(13.0, color(255, 255, 255)))
5484 .horizontal_align(TextHorizontalAlign::Center)
5485 .vertical_align(TextVerticalAlign::Center)
5486 .multiline(false),
5487 ),
5488 ],
5489 LayoutStyle::new()
5490 .with_width_percent(1.0)
5491 .with_height(180.0)
5492 .with_flex_shrink(0.0),
5493 ),
5494 );
5495}
5496
5497fn slider_options(state: &ShowcaseState, width: f32) -> widgets::SliderOptions {
5498 let mut options = widgets::SliderOptions::default().with_layout(
5499 LayoutStyle::new()
5500 .with_width(width)
5501 .with_height(24.0)
5502 .with_flex_shrink(0.0),
5503 );
5504 options.fill_color = if state.slider_trailing_color {
5505 state.slider_trailing_picker.value
5506 } else {
5507 color(42, 49, 58)
5508 };
5509 options.thumb_shape = match state.slider_thumb_shape {
5510 SliderThumbChoice::Circle => widgets::SliderThumbShape::Circle,
5511 SliderThumbChoice::Square => widgets::SliderThumbShape::Square,
5512 SliderThumbChoice::Rectangle => widgets::SliderThumbShape::Rectangle,
5513 };
5514 options
5515}
5516
5517fn slider_number_input(
5518 ui: &mut UiDocument,
5519 parent: UiNodeId,
5520 name: &'static str,
5521 input: &TextInputState,
5522 focused: FocusedTextInput,
5523 state: &ShowcaseState,
5524 width: f32,
5525) {
5526 let mut options = TextInputOptions::default();
5527 options.layout = LayoutStyle::new().with_width(width).with_height(28.0);
5528 options.text_style = text(12.0, color(230, 236, 246));
5529 options.placeholder_style = text(12.0, color(144, 156, 174));
5530 options.edit_action = Some(format!("{name}.edit").into());
5531 options.focused = state.focused_text == Some(focused);
5532 options.caret_visible = caret_visible(state.caret_phase);
5533 widgets::text_input(ui, parent, name, input, options);
5534}
5535
5536fn form_text_field(ui: &mut UiDocument, parent: UiNodeId, name: &'static str, value: &'static str) {
5537 let mut options = TextInputOptions::default();
5538 options.layout = LayoutStyle::new().with_width_percent(1.0).with_height(30.0);
5539 options.text_style = text(12.0, color(230, 236, 246));
5540 options.read_only = true;
5541 widgets::text_input(ui, parent, name, &TextInputState::new(value), options);
5542}
5543
5544fn slider_checkbox(
5545 ui: &mut UiDocument,
5546 parent: UiNodeId,
5547 name: &'static str,
5548 label: &'static str,
5549 checked: bool,
5550) {
5551 slider_checkbox_with_layout(
5552 ui,
5553 parent,
5554 name,
5555 label,
5556 checked,
5557 LayoutStyle::new().with_width_percent(1.0).with_height(30.0),
5558 );
5559}
5560
5561fn slider_checkbox_with_layout(
5562 ui: &mut UiDocument,
5563 parent: UiNodeId,
5564 name: &'static str,
5565 label: &'static str,
5566 checked: bool,
5567 layout: LayoutStyle,
5568) {
5569 let mut options = widgets::CheckboxOptions::default().with_action(name);
5570 options.layout = layout;
5571 options.text_style = text(12.0, color(220, 228, 238));
5572 widgets::checkbox(ui, parent, name, label, checked, options);
5573}
5574
5575fn choice_button(
5576 ui: &mut UiDocument,
5577 parent: UiNodeId,
5578 name: &'static str,
5579 label: &'static str,
5580 selected: bool,
5581) {
5582 let mut options =
5583 widgets::ButtonOptions::new(LayoutStyle::new().with_width(78.0).with_height(28.0))
5584 .with_action(name);
5585 options.visual = if selected {
5586 button_visual(48, 112, 184)
5587 } else {
5588 button_visual(38, 46, 58)
5589 };
5590 options.hovered_visual = Some(button_visual(65, 86, 106));
5591 options.pressed_visual = Some(button_visual(34, 54, 84));
5592 options.text_style = text(12.0, color(238, 244, 252));
5593 widgets::button(ui, parent, name, label, options);
5594}
5595
5596fn divider(ui: &mut UiDocument, parent: UiNodeId, name: &'static str) {
5597 ui.add_child(
5598 parent,
5599 UiNode::container(
5600 name,
5601 LayoutStyle::new()
5602 .with_width_percent(1.0)
5603 .with_height(1.0)
5604 .with_flex_shrink(0.0),
5605 )
5606 .with_visual(UiVisual::panel(color(48, 58, 72), None, 0.0)),
5607 );
5608}
5609
5610fn canvas(ui: &mut UiDocument, parent: UiNodeId) {
5611 let body = section(ui, parent, "canvas", "Canvas");
5612 let mut options = widgets::CanvasOptions::default()
5613 .with_accessibility_label("Shader canvas")
5614 .with_action("canvas.rotate")
5615 .with_aspect_ratio(16.0 / 9.0);
5616 options.layout = LayoutStyle::new()
5617 .with_width_percent(1.0)
5618 .with_height_percent(1.0)
5619 .with_flex_grow(1.0)
5620 .with_flex_shrink(1.0);
5621 options.visual = UiVisual::panel(
5622 color(18, 22, 28),
5623 Some(StrokeStyle::new(color(58, 68, 84), 1.0)),
5624 4.0,
5625 );
5626 widgets::canvas(
5627 ui,
5628 body,
5629 "canvas.shader",
5630 CanvasContent::new("canvas.shader").gpu_context(),
5631 options,
5632 );
5633}
5634
5635fn render_showcase_canvas(
5636 state: &mut ShowcaseState,
5637 context: NativeWgpuCanvasRenderContext<'_>,
5638) -> Result<CanvasRenderOutput, RenderError> {
5639 let size = context.surface_size();
5640 if state.cube.needs_render(size) {
5641 render_showcase_canvas_surface(state.cube, &context.surface)?;
5642 state.cube.mark_rendered(size);
5643 }
5644 Ok(CanvasRenderOutput::new())
5645}
5646
5647fn render_showcase_canvas_surface(
5648 cube: CanvasCubeState,
5649 surface: &WgpuCanvasContext<'_>,
5650) -> Result<(), RenderError> {
5651 let uniforms = canvas_cube_uniform_bytes(cube);
5652 surface.render_pass(
5653 WgpuCanvasRenderPass::wgsl(include_str!("shaders/showcase_canvas.wgsl"))
5654 .label(Some("showcase.canvas"))
5655 .uniform_bytes(&uniforms[..])
5656 .clear_color(Some(color(18, 22, 28))),
5657 )
5658}
5659
5660fn canvas_cube_uniform_bytes(cube: CanvasCubeState) -> [u8; 16] {
5661 let mut bytes = [0_u8; 16];
5662 bytes[0..4].copy_from_slice(&cube.yaw.to_ne_bytes());
5663 bytes[4..8].copy_from_slice(&cube.pitch.to_ne_bytes());
5664 bytes
5665}
5666
5667fn section(
5668 ui: &mut UiDocument,
5669 parent: UiNodeId,
5670 name: impl Into<String>,
5671 _title: impl Into<String>,
5672) -> UiNodeId {
5673 let name = name.into();
5674 let mut layout = LayoutStyle::column()
5675 .with_width_percent(1.0)
5676 .with_height_percent(1.0)
5677 .with_flex_grow(1.0)
5678 .gap(10.0);
5679 layout.as_taffy_style_mut().min_size.width = operad::length(0.0);
5680 layout.as_taffy_style_mut().min_size.height = operad::length(0.0);
5681 widgets::scroll_area(
5682 ui,
5683 parent,
5684 format!("{name}.section_scroll"),
5685 ScrollAxes::VERTICAL,
5686 layout,
5687 )
5688}
5689
5690fn row(ui: &mut UiDocument, parent: UiNodeId, name: impl Into<String>, gap: f32) -> UiNodeId {
5691 ui.add_child(
5692 parent,
5693 UiNode::container(name, LayoutStyle::row().with_width_percent(1.0).gap(gap)),
5694 )
5695}
5696
5697fn wrapping_row(
5698 ui: &mut UiDocument,
5699 parent: UiNodeId,
5700 name: impl Into<String>,
5701 gap: f32,
5702) -> UiNodeId {
5703 let mut layout = LayoutStyle::row().with_width_percent(1.0).gap(gap);
5704 layout.as_taffy_style_mut().flex_wrap = LayoutFlexWrap::Wrap.to_taffy();
5705 ui.add_child(parent, UiNode::container(name, layout))
5706}
5707
5708fn egui_panel_contents(
5709 ui: &mut UiDocument,
5710 parent: UiNodeId,
5711 name: &'static str,
5712 title: &'static str,
5713 offset_y: f32,
5714) {
5715 let header = ui.add_child(
5716 parent,
5717 UiNode::container(
5718 format!("{name}.egui_header"),
5719 LayoutStyle::row()
5720 .with_width_percent(1.0)
5721 .with_height(28.0)
5722 .with_padding(6.0)
5723 .with_flex_shrink(0.0),
5724 )
5725 .with_visual(UiVisual::panel(
5726 color(21, 26, 34),
5727 Some(StrokeStyle::new(color(54, 65, 80), 1.0)),
5728 0.0,
5729 )),
5730 );
5731 widgets::label(
5732 ui,
5733 header,
5734 format!("{name}.egui_title"),
5735 title,
5736 text(12.0, color(226, 234, 246)),
5737 LayoutStyle::new().with_width_percent(1.0),
5738 );
5739 let scroll = widgets::scroll_area(
5740 ui,
5741 parent,
5742 format!("{name}.scroll_area"),
5743 ScrollAxes::VERTICAL,
5744 LayoutStyle::column()
5745 .with_width_percent(1.0)
5746 .with_height(0.0)
5747 .with_flex_grow(1.0)
5748 .with_padding(8.0)
5749 .with_gap(6.0),
5750 );
5751 ui.node_mut(scroll).action = Some(format!("{name}.scroll").into());
5752 if let Some(scroll_state) = ui.node_mut(scroll).scroll.as_mut() {
5753 scroll_state.offset.y = offset_y;
5754 }
5755 for (index, line) in lorem_lines().iter().take(8).enumerate() {
5756 widgets::label(
5757 ui,
5758 scroll,
5759 format!("{name}.egui_line.{index}"),
5760 *line,
5761 TextStyle {
5762 wrap: TextWrap::None,
5763 ..text(11.0, color(190, 202, 218))
5764 },
5765 LayoutStyle::new()
5766 .with_width_percent(1.0)
5767 .with_height(22.0)
5768 .with_flex_shrink(0.0),
5769 );
5770 }
5771}
5772
5773fn button(
5774 ui: &mut UiDocument,
5775 parent: UiNodeId,
5776 name: impl Into<String>,
5777 label: impl Into<String>,
5778 action: impl Into<String>,
5779 visual: UiVisual,
5780) -> UiNodeId {
5781 let mut options = widgets::ButtonOptions::new(LayoutStyle::new().with_height(32.0))
5782 .with_action(action.into());
5783 options.visual = visual;
5784 options.hovered_visual = Some(adjusted_button_visual(visual, 58));
5785 options.pressed_visual = Some(adjusted_button_visual(visual, -62));
5786 options.pressed_hovered_visual = Some(adjusted_button_visual(visual, 8));
5787 options.text_style = text(13.0, color(246, 249, 252));
5788 widgets::button(ui, parent, name, label, options)
5789}
5790
5791fn button_visual(r: u8, g: u8, b: u8) -> UiVisual {
5792 UiVisual::panel(
5793 color(r, g, b),
5794 Some(StrokeStyle::new(color(86, 102, 124), 1.0)),
5795 4.0,
5796 )
5797}
5798
5799fn color_square_button_options(action: &'static str) -> widgets::ColorButtonOptions {
5800 widgets::ColorButtonOptions::default()
5801 .with_layout(LayoutStyle::size(30.0, 30.0).with_flex_shrink(0.0))
5802 .with_swatch_size(UiSize::new(30.0, 30.0))
5803 .with_action(action)
5804 .show_label(false)
5805}
5806
5807fn color_value_button_options(action: &'static str, width: f32) -> widgets::ColorButtonOptions {
5808 widgets::ColorButtonOptions::default()
5809 .with_layout(
5810 LayoutStyle::new()
5811 .with_width(width)
5812 .with_height(30.0)
5813 .with_flex_shrink(0.0),
5814 )
5815 .with_action(action)
5816}
5817
5818fn icon_image(icon: BuiltInIcon) -> ImageContent {
5819 ImageContent::new(icon.key()).tinted(color(220, 228, 238))
5820}
5821
5822fn adjusted_button_visual(visual: UiVisual, delta: i16) -> UiVisual {
5823 UiVisual::panel(
5824 adjust_color(visual.fill, delta),
5825 visual.stroke.map(|stroke| StrokeStyle {
5826 color: adjust_color(stroke.color, delta / 2),
5827 width: stroke.width,
5828 }),
5829 visual.corner_radius,
5830 )
5831}
5832
5833fn adjust_color(color: ColorRgba, delta: i16) -> ColorRgba {
5834 let channel = |value: u8| -> u8 { (i16::from(value) + delta).clamp(0, u8::MAX as i16) as u8 };
5835 ColorRgba::new(
5836 channel(color.r),
5837 channel(color.g),
5838 channel(color.b),
5839 color.a,
5840 )
5841}
5842
5843fn select_options() -> Vec<widgets::SelectOption> {
5844 vec![
5845 widgets::SelectOption::new("compact", "Compact"),
5846 widgets::SelectOption::new("comfortable", "Comfortable"),
5847 widgets::SelectOption::new("spacious", "Spacious"),
5848 widgets::SelectOption::new("disabled", "Disabled").disabled(),
5849 ]
5850}
5851
5852fn label_locale_options() -> Vec<widgets::SelectOption> {
5853 vec![
5854 widgets::SelectOption::new("en-US", "English"),
5855 widgets::SelectOption::new("es-MX", "Español"),
5856 widgets::SelectOption::new("fr-FR", "Français"),
5857 widgets::SelectOption::new("de-DE", "Deutsch"),
5858 widgets::SelectOption::new("it-IT", "Italiano"),
5859 widgets::SelectOption::new("pt-BR", "Português"),
5860 widgets::SelectOption::new("nl-NL", "Nederlands"),
5861 ]
5862}
5863
5864fn localized_label(locale_id: &str) -> &'static str {
5865 match locale_id {
5866 "en-US" => "Interface language: English",
5867 "fr-FR" => "Langue de l'interface : français",
5868 "de-DE" => "Sprache der Oberfläche: Deutsch",
5869 "it-IT" => "Lingua dell'interfaccia: italiano",
5870 "pt-BR" => "Idioma da interface: português",
5871 "nl-NL" => "Interfacetaal: Nederlands",
5872 _ => "Idioma de interfaz: español de México",
5873 }
5874}
5875
5876fn lorem_lines() -> [&'static str; 8] {
5877 [
5878 "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
5879 "Integer vitae arcu at neque feugiat posuere.",
5880 "Suspendisse potenti. Praesent eget sem non mauris luctus.",
5881 "Curabitur blandit, justo non gravida tristique, mi nunc.",
5882 "Donec at nibh vel sapien facilisis feugiat.",
5883 "Aliquam erat volutpat. Nam porttitor sem at ligula.",
5884 "Vivamus dictum eros vitae tortor aliquet, in tempor urna.",
5885 "Sed finibus velit non lectus efficitur, sed tempor orci.",
5886 ]
5887}
5888
5889fn menu_bar_menus(autosave: bool, grid: bool) -> Vec<widgets::MenuBarMenu> {
5890 vec![
5891 widgets::MenuBarMenu::new("file", "File", menu_items(autosave)),
5892 widgets::MenuBarMenu::new(
5893 "edit",
5894 "Edit",
5895 vec![
5896 widgets::MenuItem::command("undo", "Undo").shortcut("Ctrl+Z"),
5897 widgets::MenuItem::command("redo", "Redo").shortcut("Ctrl+Shift+Z"),
5898 ],
5899 ),
5900 widgets::MenuBarMenu::new(
5901 "view",
5902 "View",
5903 vec![widgets::MenuItem::check("grid", "Grid", grid)],
5904 ),
5905 ]
5906}
5907
5908fn menu_items(autosave: bool) -> Vec<widgets::MenuItem> {
5909 vec![
5910 widgets::MenuItem::command("new", "New").shortcut("Ctrl+N"),
5911 widgets::MenuItem::command("open", "Open").shortcut("Ctrl+O"),
5912 widgets::MenuItem::separator(),
5913 widgets::MenuItem::check("autosave", "Autosave", autosave),
5914 widgets::MenuItem::submenu(
5915 "recent",
5916 "Recent",
5917 vec![
5918 widgets::MenuItem::command("recent.one", "demo.rs"),
5919 widgets::MenuItem::command("recent.two", "notes.md"),
5920 ],
5921 ),
5922 widgets::MenuItem::command("delete", "Delete").destructive(),
5923 widgets::MenuItem::command("disabled", "Disabled").disabled(),
5924 ]
5925}
5926
5927fn menu_item_top_offset(items: &[widgets::MenuItem], index: usize) -> f32 {
5928 items
5929 .iter()
5930 .take(index)
5931 .map(|item| menu_item_height(Some(item)))
5932 .sum()
5933}
5934
5935fn menu_item_height(item: Option<&widgets::MenuItem>) -> f32 {
5936 if item.is_some_and(widgets::MenuItem::is_separator) {
5937 8.0
5938 } else {
5939 28.0
5940 }
5941}
5942
5943fn command_palette_items() -> Vec<widgets::CommandPaletteItem> {
5944 vec![
5945 widgets::CommandPaletteItem::new("open", "Open")
5946 .subtitle("Open a document")
5947 .shortcut("Ctrl+O")
5948 .keyword("file"),
5949 widgets::CommandPaletteItem::new("save", "Save")
5950 .subtitle("Write current changes")
5951 .shortcut("Ctrl+S"),
5952 widgets::CommandPaletteItem::new("format", "Format document")
5953 .subtitle("Apply source formatting")
5954 .keyword("code"),
5955 widgets::CommandPaletteItem::new("rename", "Rename symbol")
5956 .subtitle("Change every reference")
5957 .shortcut("F2"),
5958 widgets::CommandPaletteItem::new("toggle_sidebar", "Toggle sidebar")
5959 .subtitle("Show or hide the widget panel")
5960 .shortcut("Ctrl+B"),
5961 widgets::CommandPaletteItem::new("run", "Run current example")
5962 .subtitle("Launch showcase")
5963 .shortcut("Ctrl+R"),
5964 widgets::CommandPaletteItem::new("focus_canvas", "Focus canvas")
5965 .subtitle("Move interaction to the canvas window"),
5966 widgets::CommandPaletteItem::new("reset_layout", "Reset window layout")
5967 .subtitle("Restore the default showcase positions"),
5968 widgets::CommandPaletteItem::new("disabled", "Disabled command").disabled(),
5969 ]
5970}
5971
5972fn table_columns() -> Vec<widgets::TableColumn> {
5973 vec![
5974 widgets::TableColumn {
5975 id: "name".to_string(),
5976 label: "Name".to_string(),
5977 width: 160.0,
5978 },
5979 widgets::TableColumn {
5980 id: "status".to_string(),
5981 label: "Status".to_string(),
5982 width: 140.0,
5983 },
5984 widgets::TableColumn {
5985 id: "value".to_string(),
5986 label: "Value".to_string(),
5987 width: 100.0,
5988 },
5989 ]
5990}
5991
5992fn tree_items() -> Vec<widgets::TreeItem> {
5993 vec![
5994 widgets::TreeItem::new("root", "Project").with_children(vec![
5995 widgets::TreeItem::new("src", "src").with_children(vec![
5996 widgets::TreeItem::new("lib", "lib.rs"),
5997 widgets::TreeItem::new("widgets", "widgets.rs"),
5998 ]),
5999 widgets::TreeItem::new("assets", "assets").with_children(vec![
6000 widgets::TreeItem::new("shader", "shader.wgsl"),
6001 widgets::TreeItem::new("logo", "logo.png"),
6002 ]),
6003 widgets::TreeItem::new("target", "target").disabled(),
6004 ]),
6005 ]
6006}
6007
6008fn parse_calendar_date(value: &str) -> Option<CalendarDate> {
6009 let mut parts = value.split('-');
6010 let year = parts.next()?.parse().ok()?;
6011 let month = parts.next()?.parse().ok()?;
6012 let day = parts.next()?.parse().ok()?;
6013 CalendarDate::new(year, month, day)
6014}
6015
6016fn parse_table_cell(value: &str) -> Option<widgets::DataTableCellIndex> {
6017 let mut parts = value.split('.');
6018 let row = parts.next()?.parse().ok()?;
6019 let column = parts.next()?.parse().ok()?;
6020 if parts.next().is_some() {
6021 return None;
6022 }
6023 Some(widgets::DataTableCellIndex::new(row, column))
6024}
6025
6026fn unit(value: f32) -> f32 {
6027 value.clamp(0.0, 1.0)
6028}
6029
6030fn smooth_loop(phase: f32, offset: f32) -> f32 {
6031 0.5 - ((phase + offset).cos() * 0.5)
6032}
6033
6034fn create_system_clipboard() -> Option<arboard::Clipboard> {
6035 arboard::Clipboard::new().ok()
6036}
6037
6038fn profile_form_state() -> FormState {
6039 let mut form = FormState::new("profile")
6040 .with_field("name", "Operad")
6041 .with_field("email", "invalid@example")
6042 .with_field("role", "Designer");
6043 form.update_field("email", "invalid@example").unwrap();
6044 let request = form.begin_form_validation();
6045 let _ = form.apply_form_validation(
6046 FormValidationResult::new(request.generation)
6047 .with_field_messages(
6048 "email",
6049 vec![ValidationMessage::error("Use a complete email address")],
6050 )
6051 .with_form_message(ValidationMessage::warning("Unsaved profile changes")),
6052 );
6053 form
6054}Sourcepub fn warning(message: impl Into<String>) -> Self
pub fn warning(message: impl Into<String>) -> Self
Examples found in repository?
examples/showcase.rs (line 6051)
6038fn profile_form_state() -> FormState {
6039 let mut form = FormState::new("profile")
6040 .with_field("name", "Operad")
6041 .with_field("email", "invalid@example")
6042 .with_field("role", "Designer");
6043 form.update_field("email", "invalid@example").unwrap();
6044 let request = form.begin_form_validation();
6045 let _ = form.apply_form_validation(
6046 FormValidationResult::new(request.generation)
6047 .with_field_messages(
6048 "email",
6049 vec![ValidationMessage::error("Use a complete email address")],
6050 )
6051 .with_form_message(ValidationMessage::warning("Unsaved profile changes")),
6052 );
6053 form
6054}pub fn info(message: impl Into<String>) -> Self
Trait Implementations§
Source§impl Clone for ValidationMessage
impl Clone for ValidationMessage
Source§fn clone(&self) -> ValidationMessage
fn clone(&self) -> ValidationMessage
Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
Performs copy-assignment from
source. Read moreSource§impl Debug for ValidationMessage
impl Debug for ValidationMessage
Source§impl PartialEq for ValidationMessage
impl PartialEq for ValidationMessage
Source§fn eq(&self, other: &ValidationMessage) -> bool
fn eq(&self, other: &ValidationMessage) -> bool
Tests for
self and other values to be equal, and is used by ==.impl Eq for ValidationMessage
impl StructuralPartialEq for ValidationMessage
Auto Trait Implementations§
impl Freeze for ValidationMessage
impl RefUnwindSafe for ValidationMessage
impl Send for ValidationMessage
impl Sync for ValidationMessage
impl Unpin for ValidationMessage
impl UnsafeUnpin for ValidationMessage
impl UnwindSafe for ValidationMessage
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Convert
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Convert
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
Convert
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
Convert
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
Compare self to
key and return true if they are equal.