add_display::View done

This commit is contained in:
Aleksander
2025-12-19 19:01:36 +01:00
parent 9aea733452
commit e81c3945e6
11 changed files with 317 additions and 58 deletions

View File

@@ -2,16 +2,29 @@
<include src="../t_group_box.xml" /> <include src="../t_group_box.xml" />
<elements> <elements>
<div gap="8" flex_direction="column" width="100%" justify_self="center" align_items="center" justify_content="center"> <div gap="16" flex_direction="column" width="100%" justify_self="center" align_items="center" justify_content="center">
<rectangle macro="group_box"> <rectangle macro="group_box" align_items="center">
<label translation="POPUP_ADD_DISPLAY.RESOLUTION" weight="bold" size="20" /> <label translation="POPUP_ADD_DISPLAY.RESOLUTION" weight="bold" size="20" />
<Slider id="slider_width" min_value="0" max_value="10" width="250" height="24" /> <div gap="8" align_items="center">
<Slider id="slider_height" min_value="0" max_value="10" width="250" height="24" /> <div flex_direction="column" gap="8" align_items="end">
<rectangle macro="group_box" id="rect_display"> <label translation="WIDTH" />
<label id="label_display" /> <label translation="HEIGHT" />
</div>
<div flex_direction="column" gap="8">
<Slider show_value="0" id="slider_width" value="2" min_value="0" max_value="6" width="250" height="24" />
<Slider show_value="0" id="slider_height" value="2" min_value="0" max_value="6" width="250" height="24" />
</div>
<div flex_direction="column" gap="8">
<label id="label_width" weight="bold" text="px" />
<label id="label_height" weight="bold" text="px" />
</div>
</div>
<CheckBox id="cb_portrait" translation="DISPLAY_PORTRAIT_MODE" height="24" />
<rectangle min_width="64" min_height="32" macro="group_box" id="rect_display" align_items="center" justify_content="center" padding="0" overflow="hidden">
<label id="label_display" align="center" />
</rectangle> </rectangle>
</rectangle> </rectangle>
<Button id="btn_confirm" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_left="12" padding_right="12"> <Button id="btn_confirm" color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_left="12" padding_right="12" min_height="32">
<sprite src_builtin="dashboard/display.svg" width="32" height="32" /> <sprite src_builtin="dashboard/display.svg" width="32" height="32" />
<label translation="ADD_DISPLAY" weight="bold" size="17" shadow="#00000099" /> <label translation="ADD_DISPLAY" weight="bold" size="17" shadow="#00000099" />
</Button> </Button>

View File

@@ -2,9 +2,9 @@
<include src="../t_group_box.xml" /> <include src="../t_group_box.xml" />
<elements> <elements>
<rectangle macro="group_box" flex_direction="row"> <rectangle macro="group_box" flex_direction="row" align_items="center">
<div id="list_parent" /> <div id="list_parent" gap="8" flex_direction="row" flex_wrap="wrap" flex_grow="1" />
<Button id="btn_add" sprite_src_builtin="dashboard/add.svg" tooltip="ADD_DISPLAY" height="100%" width="32" /> <Button id="btn_add" sprite_src_builtin="dashboard/add.svg" tooltip="ADD_DISPLAY" height="100%" min_width="32" />
</rectangle> </rectangle>
</elements> </elements>
</layout> </layout>

View File

@@ -52,5 +52,8 @@
"ADD_DISPLAY": "Bildschirm hinzufügen", "ADD_DISPLAY": "Bildschirm hinzufügen",
"POPUP_ADD_DISPLAY": { "POPUP_ADD_DISPLAY": {
"RESOLUTION": "Auflösung" "RESOLUTION": "Auflösung"
} },
"WIDTH": "Breite",
"HEIGHT": "Höhe",
"DISPLAY_PORTRAIT_MODE": "Porträtmodus"
} }

View File

@@ -2,10 +2,8 @@
"ACTIONS": { "ACTIONS": {
"RECENTER_PLAYSPACE": "Re-center playspace" "RECENTER_PLAYSPACE": "Re-center playspace"
}, },
"POPUP_ADD_DISPLAY": {
"RESOLUTION": "Resolution"
},
"ADD_DISPLAY": "Add display", "ADD_DISPLAY": "Add display",
"DISPLAY_PORTRAIT_MODE": "Portrait mode",
"APP_SETTINGS": { "APP_SETTINGS": {
"BRIGHTNESS": "Brightness", "BRIGHTNESS": "Brightness",
"HEADSET_SETTINGS": "Headset settings", "HEADSET_SETTINGS": "Headset settings",
@@ -44,6 +42,7 @@
}, },
"GAMES": "Games", "GAMES": "Games",
"GENERAL_SETTINGS": "General settings", "GENERAL_SETTINGS": "General settings",
"HEIGHT": "Height",
"HELLO": "Hello!", "HELLO": "Hello!",
"HELLO_USER": "Hello, {USER}!", "HELLO_USER": "Hello, {USER}!",
"HOME_SCREEN": "Home", "HOME_SCREEN": "Home",
@@ -51,6 +50,10 @@
"LIST_OF_PROCESSES": "Process list", "LIST_OF_PROCESSES": "Process list",
"MONADO_RUNTIME": "„Monado” runtime", "MONADO_RUNTIME": "„Monado” runtime",
"NO_DISPLAYS_FOUND": "No displays found", "NO_DISPLAYS_FOUND": "No displays found",
"POPUP_ADD_DISPLAY": {
"RESOLUTION": "Resolution"
},
"PROCESSES": "Processes", "PROCESSES": "Processes",
"SETTINGS": "Settings" "SETTINGS": "Settings",
"WIDTH": "Width"
} }

View File

@@ -52,5 +52,8 @@
"ADD_DISPLAY": "Agregar pantalla", "ADD_DISPLAY": "Agregar pantalla",
"POPUP_ADD_DISPLAY": { "POPUP_ADD_DISPLAY": {
"RESOLUTION": "Resolución" "RESOLUTION": "Resolución"
} },
"WIDTH": "Ancho",
"HEIGHT": "Altura",
"DISPLAY_PORTRAIT_MODE": "Modo retrato"
} }

View File

@@ -52,5 +52,8 @@
"ADD_DISPLAY": "ディスプレイを追加", "ADD_DISPLAY": "ディスプレイを追加",
"POPUP_ADD_DISPLAY": { "POPUP_ADD_DISPLAY": {
"RESOLUTION": "解像度" "RESOLUTION": "解像度"
} },
"WIDTH": "幅",
"HEIGHT": "高さ",
"DISPLAY_PORTRAIT_MODE": "縦向きモード"
} }

View File

@@ -46,11 +46,14 @@
"ACTIONS": { "ACTIONS": {
"RECENTER_PLAYSPACE": "Wycentruj przestrzeń" "RECENTER_PLAYSPACE": "Wycentruj przestrzeń"
}, },
"LIST_OF_DISPLAYS": "Lista wyświetlaczy", "LIST_OF_DISPLAYS": "Lista monitorów",
"LIST_OF_PROCESSES": "Lista procesów", "LIST_OF_PROCESSES": "Lista procesów",
"NO_DISPLAYS_FOUND": "Brak monitorów", "NO_DISPLAYS_FOUND": "Brak monitorów",
"ADD_DISPLAY": "Dodaj monitor", "ADD_DISPLAY": "Dodaj monitor",
"POPUP_ADD_DISPLAY": { "POPUP_ADD_DISPLAY": {
"RESOLUTION": "Rozdzielczość" "RESOLUTION": "Rozdzielczość"
} },
"WIDTH": "Szerokość",
"HEIGHT": "Wysokość",
"DISPLAY_PORTRAIT_MODE": "Tryb pionowy"
} }

View File

@@ -1,19 +1,26 @@
use std::{collections::HashMap, rc::Rc}; use std::rc::Rc;
use anyhow::Context;
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::{button::ComponentButton, slider::ComponentSlider}, components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
event::StyleSetRequest,
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState}, parser::{Fetchable, ParseDocumentParams, ParserState},
taffy::prelude::length,
widget::{label::WidgetLabel, rectangle::WidgetRectangle}, widget::{label::WidgetLabel, rectangle::WidgetRectangle},
}; };
use crate::{frontend::FrontendTasks, tab::TabUpdateParams, task::Tasks}; use crate::{frontend::FrontendTasks, task::Tasks};
#[derive(Clone)] #[derive(Clone)]
enum Task { enum Task {
Confirm, Confirm,
SetWidth(u16),
SetHeight(u16),
SetPortrait(bool),
} }
pub struct View { pub struct View {
@@ -21,7 +28,25 @@ pub struct View {
pub state: ParserState, pub state: ParserState,
tasks: Tasks<Task>, tasks: Tasks<Task>,
frontend_tasks: FrontendTasks, frontend_tasks: FrontendTasks,
on_submit: Rc<dyn Fn()>, on_submit: Rc<dyn Fn(Result)>,
cur_raw_width: u16,
cur_raw_height: u16,
cur_display_name: String,
cur_portrait: bool,
id_label_width: WidgetID,
id_label_height: WidgetID,
id_label_display_name: WidgetID,
id_rect_display: WidgetID,
id_label_display: WidgetID,
}
#[derive(Clone)]
pub struct Result {
pub width: u16,
pub height: u16,
pub display_name: String,
} }
pub struct Params<'a> { pub struct Params<'a> {
@@ -29,9 +54,13 @@ pub struct Params<'a> {
pub frontend_tasks: FrontendTasks, pub frontend_tasks: FrontendTasks,
pub layout: &'a mut Layout, pub layout: &'a mut Layout,
pub parent_id: WidgetID, pub parent_id: WidgetID,
pub on_submit: Rc<dyn Fn()>, pub on_submit: Rc<dyn Fn(Result)>,
} }
const RES_COUNT: usize = 7;
const RES_WIDTHS: [u16; RES_COUNT] = [512, 854, 1280, 1600, 1920, 2560, 3840];
const RES_HEIGHTS: [u16; 7] = [256, 480, 720, 900, 1080, 1440, 2160];
impl View { impl View {
pub fn new(params: Params) -> anyhow::Result<Self> { pub fn new(params: Params) -> anyhow::Result<Self> {
let doc_params = &ParseDocumentParams { let doc_params = &ParseDocumentParams {
@@ -46,34 +75,176 @@ impl View {
let slider_width = state.fetch_component_as::<ComponentSlider>("slider_width")?; let slider_width = state.fetch_component_as::<ComponentSlider>("slider_width")?;
let slider_height = state.fetch_component_as::<ComponentSlider>("slider_height")?; let slider_height = state.fetch_component_as::<ComponentSlider>("slider_height")?;
let label_display_name = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_display_name")?; let id_label_width = state.get_widget_id("label_width")?;
let rect_display = state.fetch_widget_as::<WidgetRectangle>(&params.layout.state, "rect_display"); let id_label_height = state.get_widget_id("label_height")?;
let label_display = state.fetch_widget_as::<WidgetLabel>(&params.layout.state, "label_display"); let id_label_display_name = state.get_widget_id("label_display_name")?;
let id_rect_display = state.get_widget_id("rect_display")?;
let id_label_display = state.get_widget_id("label_display")?;
let btn_confirm = state.fetch_component_as::<ComponentButton>("btn_confirm")?; let btn_confirm = state.fetch_component_as::<ComponentButton>("btn_confirm")?;
let cb_portrait = state.fetch_component_as::<ComponentCheckbox>("cb_portrait")?;
tasks.handle_button(btn_confirm, Task::Confirm); tasks.handle_button(btn_confirm, Task::Confirm);
Ok(Self { // width
slider_width.on_value_changed({
let tasks = tasks.clone();
Box::new(move |_c, e| {
tasks.push(Task::SetWidth(RES_WIDTHS[e.value as usize]));
Ok(())
})
});
// height
slider_height.on_value_changed({
let tasks = tasks.clone();
Box::new(move |_c, e| {
tasks.push(Task::SetHeight(RES_HEIGHTS[e.value as usize]));
Ok(())
})
});
cb_portrait.on_toggle({
let tasks = tasks.clone();
Box::new(move |_c, e| {
tasks.push(Task::SetPortrait(e.checked));
Ok(())
})
});
let mut res = Self {
state, state,
tasks, tasks,
frontend_tasks: params.frontend_tasks, frontend_tasks: params.frontend_tasks,
on_submit: params.on_submit, on_submit: params.on_submit,
}) cur_raw_width: RES_WIDTHS[2],
cur_raw_height: RES_HEIGHTS[2],
cur_display_name: String::new(),
cur_portrait: false,
id_label_width,
id_label_height,
id_label_display_name,
id_rect_display,
id_label_display,
};
res.update_ui(params.layout);
Ok(res)
} }
pub fn update(&mut self) -> anyhow::Result<()> { pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> {
for task in self.tasks.drain() { for task in self.tasks.drain() {
match task { match task {
Task::Confirm => self.confirm(), Task::Confirm => self.confirm(),
Task::SetWidth(w) => {
self.cur_raw_width = w;
self.update_ui_res(layout)?;
}
Task::SetHeight(h) => {
self.cur_raw_height = h;
self.update_ui_res(layout)?;
}
Task::SetPortrait(p) => {
self.cur_portrait = p;
self.update_ui_res(layout)?;
}
} }
} }
Ok(()) Ok(())
} }
} }
// greatest common divisor
fn gcd(a: u16, b: u16) -> u16 {
let (mut a, mut b) = (a, b);
while b != 0 {
let temp = b;
b = a % b;
a = temp;
}
a
}
// aspect ratio calculation
// e.g. returns (16, 9) for input values [1280, 720]
fn aspect_ratio(width: u16, height: u16) -> (u16, u16) {
let gcd = gcd(width, height);
(width / gcd, height / gcd)
}
impl View { impl View {
fn confirm(&mut self) { fn confirm(&mut self) {
log::info!("confirm"); let (width, height) = self.get_wh();
(*self.on_submit)(); (*self.on_submit)(Result {
width,
height,
display_name: self.cur_display_name.clone(),
});
}
fn update_ui(&mut self, layout: &mut Layout) -> Option<()> {
let mut c = layout.start_common();
let (cur_width, cur_height) = self.get_wh();
{
let mut common = c.common();
let mut label_width = common.state.widgets.get_as::<WidgetLabel>(self.id_label_width)?;
let mut label_height = common.state.widgets.get_as::<WidgetLabel>(self.id_label_height)?;
let mut label_display_name = common.state.widgets.get_as::<WidgetLabel>(self.id_label_display_name)?;
let mut label_display = common.state.widgets.get_as::<WidgetLabel>(self.id_label_display)?;
// todo?
self.cur_display_name = format!("wvr-{}x{}", cur_width, cur_height);
label_width.set_text(
&mut common,
Translation::from_raw_text_string(format!("{}px", self.cur_raw_width)),
);
label_height.set_text(
&mut common,
Translation::from_raw_text_string(format!("{}px", self.cur_raw_height)),
);
let aspect = aspect_ratio(cur_width, cur_height);
label_display.set_text(
&mut common,
Translation::from_raw_text_string(format!("{}x{}\n{}:{}", cur_width, cur_height, aspect.0, aspect.1)),
);
label_display_name.set_text(&mut common, Translation::from_raw_text(&self.cur_display_name));
let mult = 0.1;
common.alterables.set_style(
self.id_rect_display,
StyleSetRequest::Width(length(cur_width as f32 * mult)),
);
common.alterables.set_style(
self.id_rect_display,
StyleSetRequest::Height(length(cur_height as f32 * mult)),
);
}
c.finish().ok()?;
Some(())
}
fn update_ui_res(&mut self, layout: &mut Layout) -> anyhow::Result<()> {
self.update_ui(layout).context("failed to update ui")?;
Ok(())
}
fn get_wh(&self) -> (u16, u16) {
if self.cur_portrait {
(self.cur_raw_height, self.cur_raw_width)
} else {
(self.cur_raw_width, self.cur_raw_height)
}
} }
} }

View File

@@ -1,18 +1,24 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use wayvr_ipc::packet_server::WvrDisplay; use wayvr_ipc::{
packet_client::{AttachTo, WvrDisplayCreateParams},
packet_server::WvrDisplay,
};
use wgui::{ use wgui::{
assets::AssetPath, assets::AssetPath,
components::button::ComponentButton, components::{self, button::ComponentButton},
drawing::Color,
globals::WguiGlobals, globals::WguiGlobals,
i18n::Translation, i18n::Translation,
layout::{Layout, WidgetID}, layout::{Layout, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState}, parser::{Fetchable, ParseDocumentParams, ParserState},
renderer_vk::text::{FontWeight, TextStyle}, renderer_vk::text::{FontWeight, HorizontalAlign, TextStyle},
taffy, taffy::{self, prelude::length},
widget::{ widget::{
ConstructEssentials,
label::{WidgetLabel, WidgetLabelParams}, label::{WidgetLabel, WidgetLabelParams},
rectangle::{WidgetRectangle, WidgetRectangleParams}, rectangle::{WidgetRectangle, WidgetRectangleParams},
util::WLength,
}, },
}; };
use wlx_common::dash_interface; use wlx_common::dash_interface;
@@ -28,7 +34,7 @@ use crate::{
#[derive(Clone)] #[derive(Clone)]
enum Task { enum Task {
AddDisplay, AddDisplay,
AddDisplayFinish, AddDisplayFinish(add_display::Result),
Refresh, Refresh,
} }
@@ -68,7 +74,6 @@ impl View {
let btn_add = parser_state.fetch_component_as::<ComponentButton>("btn_add")?; let btn_add = parser_state.fetch_component_as::<ComponentButton>("btn_add")?;
tasks.handle_button(btn_add, Task::AddDisplay); tasks.handle_button(btn_add, Task::AddDisplay);
tasks.push(Task::Refresh); tasks.push(Task::Refresh);
let state = Rc::new(RefCell::new(State { view_add_display: None })); let state = Rc::new(RefCell::new(State { view_add_display: None }));
@@ -96,7 +101,7 @@ impl View {
for task in tasks { for task in tasks {
match task { match task {
Task::AddDisplay => self.add_display(), Task::AddDisplay => self.add_display(),
Task::AddDisplayFinish => self.add_display_finish()?, Task::AddDisplayFinish(result) => self.add_display_finish(interface, result)?,
Task::Refresh => self.refresh(layout, interface)?, Task::Refresh => self.refresh(layout, interface)?,
} }
} }
@@ -104,7 +109,7 @@ impl View {
let mut state = self.state.borrow_mut(); let mut state = self.state.borrow_mut();
if let Some((_, view)) = &mut state.view_add_display { if let Some((_, view)) = &mut state.view_add_display {
view.update()?; view.update(layout)?;
} }
Ok(()) Ok(())
@@ -113,27 +118,48 @@ impl View {
fn fill_display_list( fn fill_display_list(
globals: &WguiGlobals, globals: &WguiGlobals,
parent: WidgetID, ess: &mut ConstructEssentials,
layout: &mut Layout,
list: Vec<WvrDisplay>, list: Vec<WvrDisplay>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
let accent_color = globals.defaults().accent_color;
for entry in list { for entry in list {
let (rect, _) = layout.add_child( let aspect = entry.width as f32 / entry.height as f32;
parent,
WidgetRectangle::create(WidgetRectangleParams { ..Default::default() }), let height = 96.0;
taffy::Style { let width = height * aspect;
align_items: Some(taffy::AlignItems::Center),
justify_content: Some(taffy::JustifyContent::Center), let (widget_button, button) = components::button::construct(
ess,
components::button::Params {
color: Some(accent_color.with_alpha(0.2)),
border_color: Some(accent_color),
style: taffy::Style {
align_items: Some(taffy::AlignItems::Center),
justify_content: Some(taffy::JustifyContent::Center),
size: taffy::Size {
width: length(width),
height: length(height),
},
..Default::default()
},
..Default::default() ..Default::default()
}, },
)?; )?;
button.on_click(Box::new(move |_, _| {
log::error!("display options todo");
Ok(())
}));
let label_name = WidgetLabel::create( let label_name = WidgetLabel::create(
&mut globals.get(), &mut globals.get(),
WidgetLabelParams { WidgetLabelParams {
content: Translation::from_raw_text(&entry.name), content: Translation::from_raw_text(&entry.name),
style: TextStyle { style: TextStyle {
weight: Some(FontWeight::Bold), weight: Some(FontWeight::Bold),
wrap: true,
align: Some(HorizontalAlign::Center),
..Default::default() ..Default::default()
}, },
}, },
@@ -147,8 +173,10 @@ fn fill_display_list(
}, },
); );
layout.add_child(rect.id, label_name, Default::default())?; ess.layout.add_child(widget_button.id, label_name, Default::default())?;
layout.add_child(rect.id, label_resolution, Default::default())?; ess
.layout
.add_child(widget_button.id, label_resolution, Default::default())?;
} }
Ok(()) Ok(())
@@ -162,7 +190,14 @@ impl View {
let frontend_tasks = self.frontend_tasks.clone(); let frontend_tasks = self.frontend_tasks.clone();
let globals = self.globals.clone(); let globals = self.globals.clone();
let state = self.state.clone(); let state = self.state.clone();
let tasks = self.tasks.clone();
let on_submit = {
let tasks = self.tasks.clone();
Rc::new(move |result| {
tasks.push(Task::AddDisplayFinish(result));
tasks.push(Task::Refresh);
})
};
Rc::new(move |data| { Rc::new(move |data| {
state.borrow_mut().view_add_display = Some(( state.borrow_mut().view_add_display = Some((
@@ -172,7 +207,7 @@ impl View {
globals: globals.clone(), globals: globals.clone(),
layout: data.layout, layout: data.layout,
parent_id: data.id_content, parent_id: data.id_content,
on_submit: tasks.make_callback(Task::AddDisplayFinish), on_submit: on_submit.clone(),
})?, })?,
)); ));
Ok(()) Ok(())
@@ -181,7 +216,18 @@ impl View {
})); }));
} }
fn add_display_finish(&mut self) -> anyhow::Result<()> { fn add_display_finish(
&mut self,
interface: &mut Box<dyn dash_interface::DashInterface>,
result: add_display::Result,
) -> anyhow::Result<()> {
interface.display_create(WvrDisplayCreateParams {
width: result.width,
height: result.height,
name: result.display_name,
attach_to: AttachTo::None,
scale: None,
})?;
self.state.borrow_mut().view_add_display = None; self.state.borrow_mut().view_add_display = None;
Ok(()) Ok(())
} }
@@ -199,7 +245,14 @@ impl View {
if list.is_empty() { if list.is_empty() {
text = Some(Translation::from_translation_key("NO_DISPLAYS_FOUND")) text = Some(Translation::from_translation_key("NO_DISPLAYS_FOUND"))
} else { } else {
fill_display_list(&self.globals, self.id_list_parent, layout, list)? fill_display_list(
&self.globals,
&mut ConstructEssentials {
layout,
parent: self.id_list_parent,
},
list,
)?
} }
} }
Err(e) => text = Some(Translation::from_raw_text(&format!("Error: {:?}", e))), Err(e) => text = Some(Translation::from_raw_text(&format!("Error: {:?}", e))),

View File

@@ -63,7 +63,7 @@ impl Default for Params<'_> {
} }
pub struct ButtonClickEvent {} pub struct ButtonClickEvent {}
pub type ButtonClickCallback = Box<dyn FnMut(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>; pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
pub struct Colors { pub struct Colors {
pub color: drawing::Color, pub color: drawing::Color,
@@ -351,7 +351,7 @@ fn register_event_mouse_release(
state.down = false; state.down = false;
if state.hovered if state.hovered
&& let Some(on_click) = &mut state.on_click && let Some(on_click) = &state.on_click
{ {
anim_hover( anim_hover(
rect, rect,

View File

@@ -26,13 +26,20 @@ impl Translation {
} }
} }
pub fn from_raw_text_rc(text: Rc<str>) -> Self { pub const fn from_raw_text_rc(text: Rc<str>) -> Self {
Self { Self {
text, text,
translated: false, translated: false,
} }
} }
pub fn from_raw_text_string(text: String) -> Self {
Self {
text: text.into(),
translated: false,
}
}
pub fn from_raw_text(text: &str) -> Self { pub fn from_raw_text(text: &str) -> Self {
Self { Self {
text: Rc::from(text), text: Rc::from(text),