Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement filter for file-type input's accept attribute #11757

Merged
merged 1 commit into from Jun 20, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -22,8 +22,8 @@ ipc-channel = {git = "https://github.com/servo/ipc-channel"}
lazy_static = "0.2"
log = "0.3.5"
matches = "0.1"
mime = "0.2.0"
mime_guess = "1.6.0"
mime = "0.2.1"
mime_guess = "1.8.0"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
openssl = "0.7.6"
@@ -25,37 +25,51 @@ pub trait FileManagerThreadFactory<UI: 'static + UIProvider> {
}

pub trait UIProvider where Self: Sync {
fn open_file_dialog(&self, path: &str,
filter: Option<(&[&str], &str)>) -> Option<String>;
fn open_file_dialog(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<String>;

fn open_file_dialog_multi(&self, path: &str,
filter: Option<(&[&str], &str)>) -> Option<Vec<String>>;
fn open_file_dialog_multi(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<Vec<String>>;
}

pub struct TFDProvider;

impl UIProvider for TFDProvider {
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn open_file_dialog(&self, path: &str,
filter: Option<(&[&str], &str)>) -> Option<String> {
tinyfiledialogs::open_file_dialog("Pick a file", path, filter)
fn open_file_dialog(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<String> {
let mut filter = vec![];
for p in patterns {
let s = "*.".to_string() + &p.0;
filter.push(s)
}

let filter_ref = &(filter.iter().map(|s| s.as_str()).collect::<Vec<&str>>()[..]);

let filter_opt = if filter.len() > 0 { Some((filter_ref, "")) } else { None };

tinyfiledialogs::open_file_dialog("Pick a file", path, filter_opt)
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
fn open_file_dialog_multi(&self, path: &str,
filter: Option<(&[&str], &str)>) -> Option<Vec<String>> {
tinyfiledialogs::open_file_dialog_multi("Pick files", path, filter)
fn open_file_dialog_multi(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<Vec<String>> {
let mut filter = vec![];
for p in patterns {
let s = "*.".to_string() + &p.0;
filter.push(s)
}

let filter_ref = &(filter.iter().map(|s| s.as_str()).collect::<Vec<&str>>()[..]);

let filter_opt = if filter.len() > 0 { Some((filter_ref, "")) } else { None };

tinyfiledialogs::open_file_dialog_multi("Pick files", path, filter_opt)
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
fn open_file_dialog(&self, _path: &str,
_filter: Option<(&[&str], &str)>) -> Option<String> {
fn open_file_dialog(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<String> {
None
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
fn open_file_dialog_multi(&self, _path: &str,
_filter: Option<(&[&str], &str)>) -> Option<Vec<String>> {
fn open_file_dialog_multi(&self, path: &str, patterns: Vec<FilterPattern>) -> Option<Vec<String>> {
None
}
}
@@ -116,9 +130,9 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
}
}

fn select_file(&mut self, _filter: Vec<FilterPattern>,
fn select_file(&mut self, patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<SelectedFile>>) {
match self.ui.open_file_dialog("", None) {
match self.ui.open_file_dialog("", patterns) {
Some(s) => {
let selected_path = Path::new(&s);

@@ -134,9 +148,9 @@ impl<UI: 'static + UIProvider> FileManager<UI> {
}
}

fn select_files(&mut self, _filter: Vec<FilterPattern>,
fn select_files(&mut self, patterns: Vec<FilterPattern>,
sender: IpcSender<FileManagerResult<Vec<SelectedFile>>>) {
match self.ui.open_file_dialog_multi("", None) {
match self.ui.open_file_dialog_multi("", patterns) {
Some(v) => {
let mut selected_paths = vec![];

@@ -19,6 +19,8 @@ pub struct SelectedFile {
pub type_string: String,
}

/// Filter for file selection
/// the content is expected to be extension (e.g, "doc", without the prefixing ".")
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FilterPattern(pub String);

@@ -37,7 +37,8 @@ ipc-channel = {git = "https://github.com/servo/ipc-channel"}
js = {git = "https://github.com/servo/rust-mozjs"}
libc = "0.2"
log = "0.3.5"
mime = "0.2.0"
mime = "0.2.1"
mime_guess = "1.8.0"
msg = {path = "../msg"}
net_traits = {path = "../net_traits"}
num-traits = "0.1.32"
@@ -32,6 +32,7 @@ use dom::nodelist::NodeList;
use dom::validation::Validatable;
use dom::virtualmethods::VirtualMethods;
use ipc_channel::ipc::{self, IpcSender};
use mime_guess;
use net_traits::IpcSend;
use net_traits::filemanager_thread::{FileManagerThreadMsg, FilterPattern};
use script_traits::ScriptMsg as ConstellationMsg;
@@ -44,6 +45,7 @@ use style::element_state::*;
use textinput::KeyReaction::{DispatchInput, Nothing, RedrawSelection, TriggerDefaultAction};
use textinput::Lines::Single;
use textinput::{TextInput, SelectionDirection};
use util::str::split_commas;

const DEFAULT_SUBMIT_VALUE: &'static str = "Submit";
const DEFAULT_RESET_VALUE: &'static str = "Reset";
@@ -1140,7 +1142,7 @@ impl Activatable for HTMLInputElement {
let mut files: Vec<Root<File>> = vec![];
let mut error = None;

let filter = filter_from_accept(self.Accept());
let filter = filter_from_accept(&self.Accept());

if self.Multiple() {
let (chan, recv) = ipc::channel().expect("Error initializing channel");
@@ -1231,9 +1233,20 @@ impl Activatable for HTMLInputElement {
}
}

fn filter_from_accept(_s: DOMString) -> Vec<FilterPattern> {
/// TODO: it means not pattern restriction now
/// Blocked by https://github.com/cybergeek94/mime_guess/issues/19
vec![]
}
// https://html.spec.whatwg.org/multipage/#attr-input-accept
fn filter_from_accept(s: &DOMString) -> Vec<FilterPattern> {
let mut filter = vec![];
for p in split_commas(s) {
if let Some('.') = p.chars().nth(0) {
filter.push(FilterPattern(p[1..].to_string()));
} else {
if let Some(exts) = mime_guess::get_mime_extensions_str(p) {

This comment has been minimized.

@Manishearth

Manishearth Jun 20, 2016

Member

There might be a better way of doing this by directly telling the OS the mime type. However, since it's not in the tfd api yet, we can just merge this for now.

This comment has been minimized.

@izgzhen

izgzhen Jun 20, 2016

Author Contributor

Actually I am a bit surprised that OS might have a concept like MIME (thinking only Web has that)

This comment has been minimized.

@Manishearth

Manishearth Jun 20, 2016

Member

Oh, that's a good point. I think you can tell the OS "image files only" though I'm not sure. You can investigate that later if you want.

for ext in exts {
filter.push(FilterPattern(ext.to_string()));
}
}
}
}

filter
}
@@ -55,6 +55,7 @@ extern crate libc;
extern crate log;
#[macro_use]
extern crate mime;
extern crate mime_guess;
extern crate msg;
extern crate net_traits;
extern crate num_traits;

Some generated files are not rendered by default. Learn more.

Some generated files are not rendered by default. Learn more.

@@ -14,11 +14,11 @@ const TEST_PROVIDER: &'static TestProvider = &TestProvider;
struct TestProvider;

impl UIProvider for TestProvider {
fn open_file_dialog(&self, _: &str, _: Option<(&[&str], &str)>) -> Option<String> {
fn open_file_dialog(&self, _path: &str, _patterns: Vec<FilterPattern>) -> Option<String> {
Some("test.txt".to_string())
}

fn open_file_dialog_multi(&self, _: &str, _: Option<(&[&str], &str)>) -> Option<Vec<String>> {
fn open_file_dialog_multi(&self, _path: &str, _patterns: Vec<FilterPattern>) -> Option<Vec<String>> {
Some(vec!["test.txt".to_string()])
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.