feat: add setting variant
This commit is contained in:
@@ -116,11 +116,14 @@ HTML-макеты → PNG: **headless Edge** (`--headless --screenshot`), он
|
||||
домена в конфиг; переключение галочек персистится
|
||||
- [x] URL-бар привязан к свойству (был захардкожен), метка чекбокса — домен
|
||||
|
||||
### E2 — страница настроек (следующий шаг)
|
||||
### E2 — страница настроек — ГОТОВО (базовая версия)
|
||||
|
||||
- [ ] Переключение вид/настройки по кнопке cog (settings-clicked)
|
||||
- [ ] Редактор списка браузеров: имя/путь/флаги (LineEdit), вкл/выкл
|
||||
- [ ] Список запомненных доменов с удалением
|
||||
- [x] Переключение вид/настройки по кнопке cog; Esc в настройках = назад;
|
||||
env `BP_VIEW=settings` для скриншотов
|
||||
- [x] Список браузеров: иконка + имя + путь (elide), удаление корзиной
|
||||
- [x] Кнопка «Автодетект»: мерж найденных по пути, без дублей
|
||||
- [x] Запомненные домены (domain → browser) с удалением
|
||||
- [ ] Редактирование браузера (имя/путь/флаги через LineEdit) — следующий шаг
|
||||
- [ ] Чтение флагов из .lnk ярлыков (крейс `lnk`) — по желанию
|
||||
- [ ] Добавить логотипы Edge/Zen в logos/ (пересоздать набор из alrrr/browser-logos)
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="m12 19-7-7 7-7" />
|
||||
<path d="M19 12H5" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 215 B |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 6h18" />
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
|
||||
<path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 294 B |
+99
@@ -38,6 +38,24 @@ fn launch(entry: &BrowserEntry, url: &str) -> std::io::Result<()> {
|
||||
cmd.arg(url).spawn().map(|_| ())
|
||||
}
|
||||
|
||||
fn refresh_models(
|
||||
browser_model: &slint::VecModel<BrowserConfig>,
|
||||
remembered_model: &slint::VecModel<RememberedEntry>,
|
||||
cfg: &Config,
|
||||
) {
|
||||
let rows: Vec<BrowserConfig> = cfg.browsers.iter().map(to_ui_config).collect();
|
||||
browser_model.set_vec(rows);
|
||||
let rows: Vec<RememberedEntry> = cfg
|
||||
.remembered
|
||||
.iter()
|
||||
.map(|(d, b)| RememberedEntry {
|
||||
domain: d.clone().into(),
|
||||
browser: b.clone().into(),
|
||||
})
|
||||
.collect();
|
||||
remembered_model.set_vec(rows);
|
||||
}
|
||||
|
||||
fn init(cfg: Config, url: Option<String>) -> State {
|
||||
let cfg = Rc::new(RefCell::new(cfg));
|
||||
|
||||
@@ -48,6 +66,7 @@ fn init(cfg: Config, url: Option<String>) -> State {
|
||||
.map(to_ui_config)
|
||||
.collect();
|
||||
let browser_model = Rc::new(slint::VecModel::<BrowserConfig>::from(ui_browsers));
|
||||
let remembered_model = Rc::new(slint::VecModel::<RememberedEntry>::from(vec![]));
|
||||
|
||||
let main_window = MainWindow::new().unwrap();
|
||||
|
||||
@@ -55,6 +74,12 @@ fn init(cfg: Config, url: Option<String>) -> State {
|
||||
main_window.global::<Theme>().set_is_dark(false);
|
||||
}
|
||||
|
||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
||||
|
||||
if std::env::var("BP_VIEW").as_deref() == Ok("settings") {
|
||||
main_window.set_settings_open(true);
|
||||
}
|
||||
|
||||
main_window.set_current_url(
|
||||
url.clone().unwrap_or_else(|| "ku6epxboctuk.github.io".into()).into(),
|
||||
);
|
||||
@@ -176,17 +201,90 @@ fn init(cfg: Config, url: Option<String>) -> State {
|
||||
}
|
||||
});
|
||||
|
||||
main_window.on_settings_clicked({
|
||||
let main_window = main_window.clone_strong();
|
||||
let browser_model = browser_model.clone();
|
||||
let remembered_model = remembered_model.clone();
|
||||
let cfg = cfg.clone();
|
||||
move || {
|
||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
||||
main_window.set_settings_open(true);
|
||||
}
|
||||
});
|
||||
|
||||
main_window.on_settings_closed({
|
||||
let main_window = main_window.clone_strong();
|
||||
move || {
|
||||
main_window.set_settings_open(false);
|
||||
}
|
||||
});
|
||||
|
||||
main_window.on_remove_browser({
|
||||
let browser_model = browser_model.clone();
|
||||
let remembered_model = remembered_model.clone();
|
||||
let cfg = cfg.clone();
|
||||
move |name| {
|
||||
let name = name.to_string();
|
||||
cfg.borrow_mut().browsers.retain(|b| b.name != name);
|
||||
config::save(&cfg.borrow());
|
||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
||||
println!("Removed browser: {name}");
|
||||
}
|
||||
});
|
||||
|
||||
main_window.on_remove_remembered({
|
||||
let browser_model = browser_model.clone();
|
||||
let remembered_model = remembered_model.clone();
|
||||
let cfg = cfg.clone();
|
||||
move |domain| {
|
||||
let domain = domain.to_string();
|
||||
cfg.borrow_mut().remembered.remove(&domain);
|
||||
config::save(&cfg.borrow());
|
||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
||||
println!("Removed remembered domain: {domain}");
|
||||
}
|
||||
});
|
||||
|
||||
main_window.on_redetect_browsers({
|
||||
let browser_model = browser_model.clone();
|
||||
let remembered_model = remembered_model.clone();
|
||||
let cfg = cfg.clone();
|
||||
move || {
|
||||
let detected = config::detect_browsers();
|
||||
let mut cfg_ref = cfg.borrow_mut();
|
||||
for entry in detected {
|
||||
if !cfg_ref
|
||||
.browsers
|
||||
.iter()
|
||||
.any(|b| b.path.eq_ignore_ascii_case(&entry.path))
|
||||
{
|
||||
cfg_ref.browsers.push(entry);
|
||||
}
|
||||
}
|
||||
config::save(&cfg_ref);
|
||||
drop(cfg_ref);
|
||||
refresh_models(&browser_model, &remembered_model, &cfg.borrow());
|
||||
println!("Re-detected browsers");
|
||||
}
|
||||
});
|
||||
|
||||
main_window.on_esc_pressed({
|
||||
let main_window = main_window.clone_strong();
|
||||
move || {
|
||||
if main_window.get_settings_open() {
|
||||
main_window.set_settings_open(false);
|
||||
} else {
|
||||
let _ = main_window.window().hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
main_window.set_browser_model(browser_model.clone().into());
|
||||
main_window.set_remembered_list(remembered_model.clone().into());
|
||||
State {
|
||||
main_window,
|
||||
browser_model,
|
||||
remembered_model,
|
||||
config: cfg,
|
||||
}
|
||||
}
|
||||
@@ -194,6 +292,7 @@ fn init(cfg: Config, url: Option<String>) -> State {
|
||||
pub struct State {
|
||||
pub main_window: MainWindow,
|
||||
pub browser_model: Rc<slint::VecModel<BrowserConfig>>,
|
||||
pub remembered_model: Rc<slint::VecModel<RememberedEntry>>,
|
||||
pub config: Rc<RefCell<Config>>,
|
||||
}
|
||||
|
||||
|
||||
+26
-2
@@ -2,7 +2,8 @@ import { Button, CheckBox, ListView } from "std-widgets.slint";
|
||||
import { BrowserItem } from "./browser_item.slint";
|
||||
import { Header } from "./header.slint";
|
||||
import { Footer } from "./footer.slint";
|
||||
import { BrowserConfig } from "./types.slint";
|
||||
import { SettingsView } from "./settings.slint";
|
||||
import { BrowserConfig, RememberedEntry } from "./types.slint";
|
||||
import { Theme } from "theme.slint";
|
||||
|
||||
export { Theme }
|
||||
@@ -62,11 +63,17 @@ export component MainWindow inherits Window {
|
||||
in-out property <bool> remember-choice: false;
|
||||
in-out property <bool> always-ask: true;
|
||||
in property <int> selected-index: 0;
|
||||
in property <[RememberedEntry]> remembered-list: [];
|
||||
in-out property <bool> settings-open: false;
|
||||
|
||||
callback launch-browser(BrowserConfig);
|
||||
callback toggle-remember(bool);
|
||||
callback toggle-always-ask(bool);
|
||||
callback settings-clicked();
|
||||
callback settings-closed();
|
||||
callback remove-browser(string);
|
||||
callback remove-remembered(string);
|
||||
callback redetect-browsers();
|
||||
callback open-clicked();
|
||||
callback request-drag();
|
||||
callback esc-pressed();
|
||||
@@ -106,7 +113,24 @@ export component MainWindow inherits Window {
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-main;
|
||||
|
||||
VerticalLayout {
|
||||
if root.settings-open: SettingsView {
|
||||
browsers: root.browser-model;
|
||||
remembered: root.remembered-list;
|
||||
back => {
|
||||
root.settings-closed();
|
||||
}
|
||||
remove-browser(name) => {
|
||||
root.remove-browser(name);
|
||||
}
|
||||
remove-remembered(domain) => {
|
||||
root.remove-remembered(domain);
|
||||
}
|
||||
redetect => {
|
||||
root.redetect-browsers();
|
||||
}
|
||||
}
|
||||
|
||||
if !root.settings-open: VerticalLayout {
|
||||
|
||||
Header {
|
||||
url: current-url;
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
import { ListView } from "std-widgets.slint";
|
||||
import { Theme } from "./theme.slint";
|
||||
import { BrowserConfig, RememberedEntry } from "./types.slint";
|
||||
|
||||
export component SettingsView inherits Rectangle {
|
||||
in property <[BrowserConfig]> browsers;
|
||||
in property <[RememberedEntry]> remembered;
|
||||
callback back();
|
||||
callback remove-browser(string);
|
||||
callback remove-remembered(string);
|
||||
callback redetect();
|
||||
|
||||
background: transparent;
|
||||
|
||||
VerticalLayout {
|
||||
padding: 16px;
|
||||
spacing: 12px;
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: 8px;
|
||||
|
||||
Rectangle {
|
||||
width: 32px;
|
||||
border-radius: 8px;
|
||||
background: back-touch.has-hover ? Theme.surface-hover : transparent;
|
||||
|
||||
back-touch := TouchArea {
|
||||
mouse-cursor: pointer;
|
||||
clicked => {
|
||||
root.back();
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: (parent.height - self.height) / 2;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
source: @image-url("../icons/arrow_left.svg");
|
||||
colorize: back-touch.has-hover ? Theme.text-main : Theme.text-muted;
|
||||
}
|
||||
}
|
||||
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
|
||||
Text {
|
||||
text: "Настройки";
|
||||
color: Theme.text-main;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: 8px;
|
||||
|
||||
Text {
|
||||
text: "Браузеры";
|
||||
color: Theme.text-muted;
|
||||
font-size: 13px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Rectangle {}
|
||||
|
||||
Rectangle {
|
||||
width: 120px;
|
||||
height: 26px;
|
||||
border-radius: 8px;
|
||||
background: redetect-touch.has-hover ? Theme.surface-hover : Theme.surface-elevated;
|
||||
border-width: 1px;
|
||||
border-color: Theme.border-main;
|
||||
|
||||
redetect-touch := TouchArea {
|
||||
mouse-cursor: pointer;
|
||||
clicked => {
|
||||
root.redetect();
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text: "Автодетект";
|
||||
color: redetect-touch.has-hover ? Theme.text-main : Theme.text-control;
|
||||
font-size: 12px;
|
||||
horizontal-alignment: center;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
vertical-stretch: 3;
|
||||
border-radius: 8px;
|
||||
background: Theme.surface-elevated;
|
||||
clip: true;
|
||||
|
||||
ListView {
|
||||
for b in root.browsers: Rectangle {
|
||||
height: 52px;
|
||||
background: transparent;
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: 12px;
|
||||
padding-right: 8px;
|
||||
spacing: 10px;
|
||||
|
||||
Image {
|
||||
width: 22px;
|
||||
y: (parent.height - self.height) / 2;
|
||||
source: b.icon;
|
||||
}
|
||||
|
||||
VerticalLayout {
|
||||
horizontal-stretch: 1;
|
||||
alignment: center;
|
||||
|
||||
Text {
|
||||
text: b.name;
|
||||
color: Theme.text-main;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: b.path;
|
||||
color: Theme.text-hint;
|
||||
font-size: 11px;
|
||||
font-family: "Consolas";
|
||||
overflow: elide;
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 28px;
|
||||
border-radius: 6px;
|
||||
background: del-touch.has-hover ? #ef444420 : transparent;
|
||||
|
||||
del-touch := TouchArea {
|
||||
mouse-cursor: pointer;
|
||||
clicked => {
|
||||
root.remove-browser(b.name);
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: (parent.height - self.height) / 2;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
source: @image-url("../icons/trash.svg");
|
||||
colorize: del-touch.has-hover ? #ef4444 : Theme.text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "Запомненные домены";
|
||||
color: Theme.text-muted;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
vertical-stretch: 2;
|
||||
border-radius: 8px;
|
||||
background: Theme.surface-elevated;
|
||||
clip: true;
|
||||
|
||||
ListView {
|
||||
for r in root.remembered: Rectangle {
|
||||
height: 40px;
|
||||
background: transparent;
|
||||
|
||||
HorizontalLayout {
|
||||
padding-left: 12px;
|
||||
padding-right: 8px;
|
||||
spacing: 10px;
|
||||
|
||||
VerticalLayout {
|
||||
horizontal-stretch: 1;
|
||||
alignment: center;
|
||||
|
||||
HorizontalLayout {
|
||||
spacing: 6px;
|
||||
|
||||
Text {
|
||||
text: r.domain;
|
||||
color: Theme.text-main;
|
||||
font-size: 13px;
|
||||
font-family: "Consolas";
|
||||
vertical-alignment: center;
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "→ " + r.browser;
|
||||
color: Theme.text-hint;
|
||||
font-size: 12px;
|
||||
vertical-alignment: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: 28px;
|
||||
border-radius: 6px;
|
||||
background: del2-touch.has-hover ? #ef444420 : transparent;
|
||||
|
||||
del2-touch := TouchArea {
|
||||
mouse-cursor: pointer;
|
||||
clicked => {
|
||||
root.remove-remembered(r.domain);
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
x: (parent.width - self.width) / 2;
|
||||
y: (parent.height - self.height) / 2;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
source: @image-url("../icons/trash.svg");
|
||||
colorize: del2-touch.has-hover ? #ef4444 : Theme.text-muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,3 +5,8 @@ export struct BrowserConfig {
|
||||
flags: string,
|
||||
is-default: bool,
|
||||
}
|
||||
|
||||
export struct RememberedEntry {
|
||||
domain: string,
|
||||
browser: string,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user