Drag and Drop
This commit is contained in:
@@ -9,6 +9,7 @@ use ui::app::App;
|
|||||||
|
|
||||||
fn main() -> iced::Result {
|
fn main() -> iced::Result {
|
||||||
iced::application("Comparador de Notas", App::update, App::view)
|
iced::application("Comparador de Notas", App::update, App::view)
|
||||||
|
.subscription(App::subscription)
|
||||||
.theme(|_app| ui::theme::tema_dark())
|
.theme(|_app| ui::theme::tema_dark())
|
||||||
.window(iced::window::Settings {
|
.window(iced::window::Settings {
|
||||||
size: iced::Size::new(1024.0, 768.0),
|
size: iced::Size::new(1024.0, 768.0),
|
||||||
|
|||||||
@@ -284,3 +284,41 @@ exceto `GerenciandoLayouts` e `Analisando`.
|
|||||||
- `ResultadoPendente` é o tipo intermediário entre a thread de análise e a UI.
|
- `ResultadoPendente` é o tipo intermediário entre a thread de análise e a UI.
|
||||||
|
|
||||||
**Não bloquear a thread principal da UI.** Qualquer operação lenta deve usar `Task`.
|
**Não bloquear a thread principal da UI.** Qualquer operação lenta deve usar `Task`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Drag-and-drop de arquivos
|
||||||
|
|
||||||
|
O iced 0.13 expõe eventos de janela para drag-and-drop via `iced::window::Event`.
|
||||||
|
O projeto os captura através de `App::subscription()` registrado em `main.rs`.
|
||||||
|
|
||||||
|
### Eventos capturados
|
||||||
|
|
||||||
|
| Evento iced | Mensagem emitida | Efeito |
|
||||||
|
|---|---|---|
|
||||||
|
| `window::Event::FileDropped(path)` | `Message::ArquivoSolto(path)` | Processa o arquivo como se fosse selecionado via botão |
|
||||||
|
| `window::Event::FileHovered(_)` | `Message::ArquivoEmHover` | Liga `App.arquivo_em_hover = true` |
|
||||||
|
| `window::Event::FilesHoveredLeft` | `Message::ArquivoHoverSaiu` | Liga `App.arquivo_em_hover = false` |
|
||||||
|
|
||||||
|
### Subscription
|
||||||
|
|
||||||
|
`App::subscription()` em `app.rs` usa `iced::event::listen_with` para filtrar apenas
|
||||||
|
os três eventos acima. Está registrado em `main.rs` via `.subscription(App::subscription)`.
|
||||||
|
|
||||||
|
### Estado de hover
|
||||||
|
|
||||||
|
O campo `App.arquivo_em_hover: bool` é `true` enquanto um arquivo está sendo arrastado
|
||||||
|
sobre a janela. A tela `screens/import.rs` usa esse campo para alterar visualmente a
|
||||||
|
`drop_zone`: borda mais brilhante (`a: 0.9`) e mais espessa (`width: 2.0`) durante o hover.
|
||||||
|
|
||||||
|
### Fluxo de processamento
|
||||||
|
|
||||||
|
`Message::ArquivoSolto` chama `self.processar_arquivo_selecionado(caminho)` — o mesmo
|
||||||
|
método chamado pelo botão de seleção de arquivo. O comportamento é idêntico: validação
|
||||||
|
de extensão, leitura de abas (XLSX) ou preview (CSV), e transição de estado.
|
||||||
|
|
||||||
|
### Como estender
|
||||||
|
|
||||||
|
Para adicionar suporte a drag-and-drop em outras telas (ex: importar layout JSON por
|
||||||
|
drag), basta verificar `App.estado` dentro do arm `Message::ArquivoSolto` antes de
|
||||||
|
chamar `processar_arquivo_selecionado`, e desviar conforme necessário.
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ pub struct App {
|
|||||||
|
|
||||||
// Resultado anterior (preservado durante reanálise)
|
// Resultado anterior (preservado durante reanálise)
|
||||||
pub resultado_anterior: Option<ResultadoAnalise>,
|
pub resultado_anterior: Option<ResultadoAnalise>,
|
||||||
|
|
||||||
|
// Drag-and-drop: indica que um arquivo está sendo arrastado sobre a janela
|
||||||
|
pub arquivo_em_hover: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for App {
|
impl Default for App {
|
||||||
@@ -116,6 +119,7 @@ impl Default for App {
|
|||||||
itens_por_pagina: 100,
|
itens_por_pagina: 100,
|
||||||
preview_arquivo: None,
|
preview_arquivo: None,
|
||||||
resultado_anterior: None,
|
resultado_anterior: None,
|
||||||
|
arquivo_em_hover: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -659,6 +663,21 @@ impl App {
|
|||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Drag-and-drop ──
|
||||||
|
Message::ArquivoSolto(caminho) => {
|
||||||
|
self.arquivo_em_hover = false;
|
||||||
|
self.processar_arquivo_selecionado(caminho)
|
||||||
|
}
|
||||||
|
Message::ArquivoEmHover => {
|
||||||
|
self.arquivo_em_hover = true;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::ArquivoHoverSaiu => {
|
||||||
|
self.arquivo_em_hover = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Sem operação ──
|
||||||
Message::Noop => Task::none(),
|
Message::Noop => Task::none(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -720,6 +739,23 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Subscription (eventos de janela) ───────────────────────────────────
|
||||||
|
|
||||||
|
pub fn subscription(&self) -> iced::Subscription<Message> {
|
||||||
|
iced::event::listen_with(|event, _status, _id| match event {
|
||||||
|
iced::Event::Window(iced::window::Event::FileDropped(path)) => {
|
||||||
|
Some(Message::ArquivoSolto(path))
|
||||||
|
}
|
||||||
|
iced::Event::Window(iced::window::Event::FileHovered(_)) => {
|
||||||
|
Some(Message::ArquivoEmHover)
|
||||||
|
}
|
||||||
|
iced::Event::Window(iced::window::Event::FilesHoveredLeft) => {
|
||||||
|
Some(Message::ArquivoHoverSaiu)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Breadcrumb ──────────────────────────────────────────────────────────
|
// ─── Breadcrumb ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
fn view_breadcrumb(&self) -> Element<'_, Message> {
|
fn view_breadcrumb(&self) -> Element<'_, Message> {
|
||||||
|
|||||||
@@ -25,6 +25,12 @@ pub enum Message {
|
|||||||
// --- Arquivo ---
|
// --- Arquivo ---
|
||||||
SelecionarArquivo,
|
SelecionarArquivo,
|
||||||
ArquivoSelecionado(PathBuf),
|
ArquivoSelecionado(PathBuf),
|
||||||
|
/// Arquivo arrastado e solto sobre a janela (drag-and-drop).
|
||||||
|
ArquivoSolto(PathBuf),
|
||||||
|
/// Arquivo está sendo arrastado sobre a janela (hover).
|
||||||
|
ArquivoEmHover,
|
||||||
|
/// Arquivo arrastado saiu da janela sem ser solto.
|
||||||
|
ArquivoHoverSaiu,
|
||||||
AbaSelecionada(String),
|
AbaSelecionada(String),
|
||||||
|
|
||||||
// --- XLSX: abas carregadas em background ---
|
// --- XLSX: abas carregadas em background ---
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
|||||||
(app.nome_arquivo.clone(), true)
|
(app.nome_arquivo.clone(), true)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let em_hover = app.arquivo_em_hover;
|
||||||
|
|
||||||
let icone_arquivo: Element<Message> = container(
|
let icone_arquivo: Element<Message> = container(
|
||||||
text(if tem_arquivo { "CSV / XLSX" } else { "Arquivo" })
|
text(if tem_arquivo { "CSV / XLSX" } else { "Arquivo" })
|
||||||
.size(12)
|
.size(12)
|
||||||
@@ -64,7 +66,7 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
|||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
column![
|
column![
|
||||||
text("Selecione um arquivo CSV ou XLSX")
|
text("Selecione ou arraste um arquivo CSV ou XLSX")
|
||||||
.size(14)
|
.size(14)
|
||||||
.color(t::TEXT_SECONDARY),
|
.color(t::TEXT_SECONDARY),
|
||||||
text("Suportado: .csv, .xlsx, .xls")
|
text("Suportado: .csv, .xlsx, .xls")
|
||||||
@@ -85,13 +87,18 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
|||||||
.style(move |_theme| iced::widget::container::Style {
|
.style(move |_theme| iced::widget::container::Style {
|
||||||
background: Some(
|
background: Some(
|
||||||
iced::Color {
|
iced::Color {
|
||||||
a: 0.05,
|
a: if em_hover { 0.12 } else { 0.05 },
|
||||||
..t::PRIMARY
|
..t::PRIMARY
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
),
|
),
|
||||||
border: iced::Border {
|
border: iced::Border {
|
||||||
color: if tem_arquivo {
|
color: if em_hover {
|
||||||
|
iced::Color {
|
||||||
|
a: 0.9,
|
||||||
|
..t::PRIMARY
|
||||||
|
}
|
||||||
|
} else if tem_arquivo {
|
||||||
iced::Color {
|
iced::Color {
|
||||||
a: 0.5,
|
a: 0.5,
|
||||||
..t::PRIMARY
|
..t::PRIMARY
|
||||||
@@ -102,7 +109,7 @@ pub fn view(app: &App) -> Element<'_, Message> {
|
|||||||
..t::BORDER
|
..t::BORDER
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
width: 1.5,
|
width: if em_hover { 2.0 } else { 1.5 },
|
||||||
radius: 8.0.into(),
|
radius: 8.0.into(),
|
||||||
},
|
},
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
Reference in New Issue
Block a user