Skip to main content

Props

Struct Props 

Source
pub struct Props { /* private fields */ }
Expand description

Typed component attributes.

Implementations§

Source§

impl Props

Source

pub fn new() -> Self

Creates an empty property collection.

Examples found in repository?
examples/chat/demo.rs (line 281)
280	fn new(editor: Rc<RefCell<Editor>>, outcome: Rc<RefCell<Option<EditOutcome>>>) -> Self {
281		Self { props: Props::new(), slot: next_slot(), editor, outcome }
282	}
283
284	/// Cells before the editor text: the two-cell prompt plus a gap —
285	/// identical on every tier.
286	const fn input_offset() -> u16 {
287		3
288	}
289
290	/// The `╰─` composer prompt composed from the tier's round border.
291	fn input_prompt(charset: Charset) -> Str {
292		let (_, _, bl, _, horizontal, _) = charset.border(Border::Round);
293		fmts!("{bl}{horizontal}")
294	}
295
296	const fn input_width(width: u16) -> u16 {
297		width.saturating_sub(Self::input_offset()).saturating_sub(1)
298	}
299
300	fn paint_picker(pc: &mut PaintCtx<'_>, rect: Rect, y: u16, editor: &Editor) {
301		let Some(picker) = editor.picker() else {
302			return;
303		};
304		let (start, suggestions) = picker.visible_suggestions();
305		let overflow = picker.len() > suggestions.len();
306		let row_right = rect
307			.x
308			.saturating_add(rect.width.saturating_sub(u16::from(overflow)));
309		let primary_width = suggestions
310			.iter()
311			.filter_map(|suggestion| match suggestion.display() {
312				SuggestionDisplay::Text(name) => Some(visible_width(name).saturating_add(2)),
313				SuggestionDisplay::Emoji { .. } => None,
314			})
315			.max()
316			.unwrap_or(12)
317			.clamp(12, 32);
318
319		for (offset, suggestion) in suggestions.iter().enumerate() {
320			let Ok(offset) = u16::try_from(offset) else {
321				break;
322			};
323			let row = y.saturating_add(offset);
324			if row >= pc.clip {
325				break;
326			}
327			let selected = start + usize::from(offset) == picker.selected();
328			let label = if selected { ink(GREEN) } else { ink(TEXT) };
329			let description = if selected { ink(GREEN) } else { ink(MUTED) };
330			pc.frame.put(
331				rect.x,
332				row,
333				if selected {
334					pc.ctx.charset.cursor()
335				} else {
336					"  "
337				},
338				label,
339			);
340			match suggestion.display() {
341				SuggestionDisplay::Text(name) => {
342					draw_line(
343						pc.frame,
344						rect.x.saturating_add(2),
345						row,
346						row_right.saturating_sub(rect.x.saturating_add(2)),
347						&[Span::new(name, label)],
348					);
349					if let Some(text) = suggestion.description()
350						&& rect.width > 40
351					{
352						let description_x = rect
353							.x
354							.saturating_add(2)
355							.saturating_add(primary_width)
356							.min(row_right);
357						draw_line(
358							pc.frame,
359							description_x,
360							row,
361							row_right.saturating_sub(description_x),
362							&[Span::new(text, description)],
363						);
364					}
365				},
366				SuggestionDisplay::Emoji { emoji, shortcode } => {
367					let mut column = pc.frame.put(rect.x.saturating_add(2), row, emoji, label);
368					column = pc.frame.put(column, row, "  ", label);
369					if shortcode.starts_with(':') {
370						pc.frame.put(column, row, shortcode, label);
371					} else {
372						column = pc.frame.put(column, row, ":", label);
373						column = pc.frame.put(column, row, shortcode, label);
374						pc.frame.put(column, row, ":", label);
375					}
376				},
377			}
378		}
379
380		if overflow && !suggestions.is_empty() {
381			let (track, thumb_glyph) = pc.ctx.charset.scrollbar();
382			let track_x = rect.x.saturating_add(rect.width.saturating_sub(1));
383			for offset in 0..suggestions.len() {
384				let Ok(offset) = u16::try_from(offset) else {
385					break;
386				};
387				pc.frame
388					.put(track_x, y.saturating_add(offset), track, ink(FAINT));
389			}
390			let thumb = picker
391				.selected()
392				.saturating_mul(suggestions.len().saturating_sub(1))
393				/ picker.len().saturating_sub(1);
394			pc.frame.put(
395				track_x,
396				y.saturating_add(u16::try_from(thumb).unwrap_or(u16::MAX)),
397				thumb_glyph,
398				ink(GREEN),
399			);
400		}
401	}
402}
403
404impl Component for DemoInput {
405	fn props(&self) -> &Props {
406		&self.props
407	}
408
409	fn props_mut(&mut self) -> &mut Props {
410		&mut self.props
411	}
412
413	fn slot(&self) -> Slot {
414		self.slot
415	}
416
417	fn measure(&mut self, _ctx: &UiContext) -> (u16, u16) {
418		(6, 40)
419	}
420
421	fn height(&mut self, _ctx: &UiContext, width: u16) -> u16 {
422		let editor = self.editor.borrow();
423		editor
424			.input_height_for(Self::input_width(width))
425			.saturating_add(editor.picker_height())
426	}
427
428	fn paint(&mut self, pc: &mut PaintCtx<'_>, rect: Rect) {
429		pc.hits
430			.push(Hit { rect, slot: self.slot, tag: HitTag::Press });
431		let editor = self.editor.borrow();
432		let input_x = rect.x.saturating_add(Self::input_offset());
433		let input_width = Self::input_width(rect.width);
434		let input_height = editor.input_height_for(input_width);
435		let theme = Theme::default();
436		let mut in_comment = false;
437		for (offset, row) in editor.view(input_width).iter().enumerate() {
438			let row_y = rect
439				.y
440				.saturating_add(u16::try_from(offset).unwrap_or(u16::MAX));
441			if row_y >= pc.clip {
442				break;
443			}
444			if offset == 0 {
445				pc.frame
446					.put(rect.x, row_y, &Self::input_prompt(pc.ctx.charset), ink(FAINT));
447			}
448			let mut spans: SmallVec<Span<'_>, 16> = SmallVec::new();
449			if editor.options().xml {
450				let (runs, next) = highlight_xml(row.text, &theme, in_comment);
451				in_comment = next;
452				push_row_spans(&editor, row.text, &runs, &mut spans);
453			} else {
454				push_row_spans(&editor, row.text, &[], &mut spans);
455			}
456			draw_line(pc.frame, input_x, row_y, input_width, &spans);
457			if let Some(cursor_column) = row.cursor_column {
458				if cursor_column >= visible_width(row.text)
459					&& let Some(hint) = editor.inline_hint()
460				{
461					let hint_x = input_x.saturating_add(cursor_column).saturating_add(1);
462					let width = input_width.saturating_sub(cursor_column.saturating_add(1));
463					draw_line(pc.frame, hint_x, row_y, width, &[Span::new(
464						hint.as_str(),
465						ink(MUTED).dim(),
466					)]);
467				}
468				pc.frame.set_cursor(
469					input_x
470						.saturating_add(cursor_column)
471						.min(rect.x.saturating_add(rect.width.saturating_sub(2))),
472					row_y,
473				);
474			}
475		}
476		Self::paint_picker(pc, rect, rect.y.saturating_add(input_height), &editor);
477	}
478
479	fn focusable(&self) -> bool {
480		true
481	}
482
483	fn key(&mut self, _ec: &mut EventCtx<'_>, key: Key) -> Flow {
484		let outcome = self.editor.borrow_mut().handle(key);
485		*self.outcome.borrow_mut() = Some(outcome);
486		// The editor owns every key while focused. In particular, an ignored
487		// picker key must not escape into `Ui`'s focus-ring navigation; the
488		// demo applies its quit policy from the recorded `EditOutcome`.
489		Flow::Consumed
490	}
491
492	fn mouse(
493		&mut self,
494		_ec: &mut EventCtx<'_>,
495		_tag: HitTag,
496		at: (u16, u16),
497		rect: Rect,
498		mouse: Mouse,
499	) -> Flow {
500		let width = Self::input_width(rect.width);
501		match mouse {
502			Mouse::Click => {
503				self.editor.borrow_mut().set_cursor_visual_row(
504					usize::from(at.1.saturating_sub(rect.y)),
505					at.0
506						.saturating_sub(rect.x.saturating_add(Self::input_offset())),
507					width,
508				);
509				Flow::Consumed
510			},
511			Mouse::WheelUp | Mouse::WheelDown => {
512				let delta = if mouse == Mouse::WheelUp { -1 } else { 1 };
513				if self
514					.editor
515					.borrow()
516					.scroll_rows(delta, width, usize::from(rect.height))
517				{
518					Flow::Consumed
519				} else {
520					Flow::Skip
521				}
522			},
523			_ => Flow::Skip,
524		}
525	}
526
527	fn paste(&mut self, _ec: &mut EventCtx<'_>, text: &str) -> Flow {
528		if matches!(self.editor.borrow_mut().insert_text(text), EditOutcome::Changed) {
529			Flow::Consumed
530		} else {
531			Flow::Skip
532		}
533	}
534}
535/// Whether the demo is working, and how the status bar's brand segment
536/// blends between its two states.
537struct WorkState {
538	working: bool,
539	/// When the current mode began; the working timer counts from here.
540	since:   Duration,
541	/// Brand foreground: [`GREEN`] while working, [`MUTED`] at rest.
542	fade:    Tween<Color>,
543}
544
545/// Powerline status split into a left brand group — spinner and session
546/// timer while working, the omp brand at rest, the foreground tweening
547/// between the two so neither swap ever snaps — and a right-docked
548/// session group (branch, context, cost). Panes too narrow for both
549/// groups fall back to one left-anchored band that sheds from the tail.
550struct DemoStatus {
551	props:   Props,
552	slot:    Slot,
553	work:    Rc<RefCell<WorkState>>,
554	model:   Rc<RefCell<Str>>,
555	charset: Charset,
556	right:   Status,
557}
558
559impl DemoStatus {
560	fn new(work: Rc<RefCell<WorkState>>, model: Rc<RefCell<Str>>, charset: Charset) -> Self {
561		let mut props = Props::new();
562		props.set(Prop::Id, STATUS_ID);
563		let right = Self::right_group(charset);
564		Self { props, slot: next_slot(), work, model, charset, right }
565	}
Source

pub fn with(self, prop: Prop, value: impl Into<PropValue>) -> Self

Returns this collection with a known property assigned.

§Panics

Panics when a textual value is invalid for the selected property.

Source

pub fn set(&mut self, prop: Prop, value: impl Into<PropValue>)

Assigns a known property.

§Panics

Panics when a textual value is invalid for the selected property.

Examples found in repository?
examples/chat/demo.rs (line 562)
560	fn new(work: Rc<RefCell<WorkState>>, model: Rc<RefCell<Str>>, charset: Charset) -> Self {
561		let mut props = Props::new();
562		props.set(Prop::Id, STATUS_ID);
563		let right = Self::right_group(charset);
564		Self { props, slot: next_slot(), work, model, charset, right }
565	}
Source

pub fn try_set(&mut self, prop: Prop, value: PropValue) -> Result<(), PropError>

Validates and assigns a known property.

§Errors

Returns PropError when a textual value cannot be parsed for the selected property.

Source

pub fn get(&self, prop: Prop) -> Option<&PropValue>

Returns the typed value assigned to a known property.

Source

pub fn unset(&mut self, prop: Prop)

Removes a known property, restoring its unset default.

Source

pub fn get_str(&self, prop: Prop) -> Option<Str>

Formats a known property value using its markup representation.

Source

pub fn with_custom( self, name: impl Into<Str>, value: impl Into<PropValue>, ) -> Self

Returns this collection with a custom property assigned.

Source

pub fn set_custom(&mut self, name: impl Into<Str>, value: impl Into<PropValue>)

Assigns or replaces a custom property.

Source

pub fn custom(&self, name: &str) -> Option<&PropValue>

Returns a custom property by its literal name.

Source

pub fn named(&self, name: &str) -> Option<&PropValue>

Returns either a known or custom property by markup name.

Source

pub fn prop_of(name: &str) -> Option<Prop>

Resolves a markup attribute name to its well-known property — the kebab-cased variant ident derived on Prop.

Source

pub fn gap(&self) -> u16

Returns the inter-child spacing, defaulting to zero.

Source

pub fn pad(&self) -> (u16, u16)

Returns vertical and horizontal padding, defaulting to zero.

Source

pub fn grow(&self) -> Option<f32>

Returns the flexible growth weight, with a bare flag meaning one.

Source

pub fn w(&self) -> Option<Dim>

Returns the preferred width as a cell or percentage dimension.

Source

pub fn min(&self) -> Option<u16>

Returns the minimum width or numeric value.

Source

pub fn max(&self) -> Option<u16>

Returns the maximum width or numeric value.

Source

pub fn h(&self) -> Option<u16>

Returns the preferred height in rows.

Source

pub fn truncate(&self) -> Option<Truncate>

Returns the truncation mode: a bare truncate flag clips the end, truncate=start clips the beginning; None disables truncation.

Source

pub fn wrap_chars(&self) -> bool

Whether text flows grapheme-exact to the width (wrap=char) like a bare terminal: every break is a byte-preserving soft wrap the renderer re-joins for native copy. Defaults to word wrapping.

Source

pub fn border(&self) -> Option<Border>

Returns the selected border glyph family.

Source

pub fn guides(&self) -> Option<Border>

Returns the tree guide connector family; a bare flag means square.

Source

pub fn bleed(&self) -> bool

Reports whether the background extends through the border.

Source

pub fn align(&self) -> Align

Returns horizontal alignment, defaulting to the start edge.

Source

pub fn title_align(&self) -> Align

Returns the border-title placement, defaulting to the start edge.

Source

pub fn footer_align(&self) -> Align

Returns the border-footer placement, defaulting to the start edge.

Source

pub fn valign(&self) -> Option<VAlign>

Returns the configured vertical alignment.

Source

pub fn id(&self) -> Option<&Str>

Returns the stable component identifier.

Source

pub fn title(&self) -> Option<&Str>

Returns the user-facing component title.

Source

pub fn footer(&self) -> Option<&Str>

Returns the footer shown on a framed container’s bottom border.

Source

pub fn angle(&self) -> u16

Normalized gradient direction in screen degrees.

Source

pub fn anim(&self) -> Option<Duration>

Transition duration for animatable properties, when anim is set. A bare anim flag selects 200ms.

Source

pub fn ease(&self) -> Easing

Easing curve for anim transitions, defaulting to ease-out — the natural shape for state changes that should land softly.

Source

pub fn spin(&self) -> Option<Duration>

Gradient rotation period, when spin is set. A bare spin flag selects one revolution every 3 seconds.

Source

pub fn shimmer(&self) -> Option<Duration>

Brightness-crest sweep period, when shimmer is set. A bare shimmer flag selects one sweep every 2 seconds.

Source

pub fn reveal(&self) -> Option<Duration>

Streamed-text reveal catch-up horizon, when reveal is set. A bare reveal flag selects 250ms; reveal=0 shows new text immediately.

Source

pub fn lift(&self) -> u16

Rows of hover elevation, with a bare flag meaning one.

Source

pub fn flag(&self, prop: Prop) -> bool

Reports whether a boolean property is enabled.

Source

pub fn str_of(&self, prop: Prop) -> Option<&Str>

Returns the textual payload of a property.

Examples found in repository?
examples/companies.rs (line 117)
107fn build_ui(viewport: Size, context: UiContext) -> Ui {
108	let ids = PROVIDERS
109		.iter()
110		.enumerate()
111		.map(|(index, provider)| {
112			(format!("{ASSET_DIR}/{}.png", provider.id), u32::try_from(index + 1).unwrap())
113		})
114		.collect::<HashMap<_, _>>();
115	let elements = Elements::builder()
116		.with("logo", move |_: &str, props: Props, _: Vec<Cached>| {
117			let source = props.str_of(Prop::Src).map_or("", |value| value.as_str());
118			let id = ids.get(source).copied().unwrap_or(1);
119			Box::new(
120				Img::new()
121					.with_str(Prop::Src, source)
122					.with(Prop::W, 4_u16)
123					.kitty(id, 2, 4),
124			) as Box<dyn omp_tui::Component>
125		})
126		.build();
127	let root = dom! {
128		<col gap=1>
129			<row gap=1>
130				<i:log-in/>
131				<text bold fg="accent..info">{"Choose a provider"}</text>
132				<text dim>{format!("{} providers", PROVIDERS.len())}</text>
133			</row>
134			<scroll id={SCROLL_ID} h={scroll_height(viewport)}>
135				<row wrap gap=1 justify=center>
136					for provider in PROVIDERS.iter() {
137						<box focus id={provider.id} w={CARD_W} border=round bc="muted..muted"
138							hover="#38bdf8..#c084fc" lift=1 anim=220 ease=in-out
139							align=center pad-x=1>
140							<logo src={format!("{ASSET_DIR}/{}.png", provider.id)}/>
141							<text bold truncate align=center>{provider.name}</text>
142						</box>
143					}
144				</row>
145			</scroll>
146			<row gap=2>
147				<text dim>{"↹/←→/↑↓ pick · ↵ login · wheel scroll · Ctrl-C quit"}</text>
148				<text id={HUD_ID} dim>{"repaint: 0 cells"}</text>
149			</row>
150		</col>
151	};
152	let context = UiContext { elements, ..context };
153	Ui::from_root(root, viewport.width, context)
154}
Source

pub fn style(&self, theme: &Theme) -> Style

Resolves colors and text attributes into a render style.

Source

pub fn edge(&self, theme: &Theme) -> Option<Color>

Resolves the border color from either supported attribute name.

Trait Implementations§

Source§

impl Clone for Props

Source§

fn clone(&self) -> Props

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Props

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Props

Source§

fn default() -> Props

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for Props

§

impl RefUnwindSafe for Props

§

impl Send for Props

§

impl Sync for Props

§

impl Unpin for Props

§

impl UnsafeUnpin for Props

§

impl UnwindSafe for Props

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

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>

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)

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)

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
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.