@@ -3,7 +3,6 @@ name = "radiate" | |||
version = "0.2.0" | |||
authors = ["Colin Reeder <colin@vpzom.click>"] | |||
edition = "2018" | |||
build = "src/build.rs" | |||
[dependencies] | |||
qmetaobject = "0.0.5" | |||
@@ -14,6 +13,8 @@ huey = { git = "https://git.vpzom.click/vpzom/huey", rev = "0b62be" } | |||
# huey = { path = "../hueclient" } | |||
futures = "0.1.25" | |||
try_future = "0.1.3" | |||
gettext = "0.3.0" | |||
fluent-locale = "0.4.1" | |||
[build-dependencies] | |||
cpp_build = "0.5" |
@@ -0,0 +1,11 @@ | |||
mod build_qmo { | |||
include!("./build/build_qmo.rs"); | |||
} | |||
mod build_translations { | |||
include!("./build/build_translations.rs"); | |||
} | |||
fn main() { | |||
build_qmo::main(); | |||
build_translations::main(); | |||
} |
@@ -0,0 +1,41 @@ | |||
#: qml/Main.qml:74 | |||
msgid "Connect to Bridge" | |||
msgstr "Konekti al Ponton" | |||
#: qml/Main.qml:80 | |||
msgid "Searching for Hue bridges..." | |||
msgstr "Serĉas Hue-pontojn..." | |||
#: qml/Main.qml:89 | |||
msgid "Found a bridge. Press the button to pair." | |||
msgid_plural "Found %1 bridges. Press the button to pair." | |||
msgstr[0] "Trovis ponton. Premu la butonon al parigi." | |||
msgstr[1] "Trovis %1 pontojn. Premu la butonon al parigi." | |||
#: qml/Main.qml:95 | |||
msgid "Error: %1" | |||
msgstr "Eraro: %1" | |||
#: qml/Main.qml:100 | |||
msgid "Connected." | |||
msgstr "Konektis." | |||
#: qml/Main.qml:211 | |||
msgid "Lights" | |||
msgstr "Lumoj" | |||
#: qml/Main.qml:212 | |||
msgid "Groups" | |||
msgstr "Grupoj" | |||
#: qml/Main.qml:338 | |||
msgid "Brightness" | |||
msgstr "Brilo" | |||
#: qml/Main.qml:366 | |||
msgid "Hue" | |||
msgstr "Nuanco" | |||
#: qml/Main.qml:394 | |||
msgid "Saturation" | |||
msgstr "Satureco" |
@@ -0,0 +1,61 @@ | |||
# SOME DESCRIPTIVE TITLE. | |||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER | |||
# This file is distributed under the same license as the PACKAGE package. | |||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. | |||
# | |||
#, fuzzy | |||
msgid "" | |||
msgstr "" | |||
"Project-Id-Version: PACKAGE VERSION\n" | |||
"Report-Msgid-Bugs-To: \n" | |||
"POT-Creation-Date: 2019-02-27 20:24-0700\n" | |||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | |||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |||
"Language-Team: LANGUAGE <LL@li.org>\n" | |||
"Language: \n" | |||
"MIME-Version: 1.0\n" | |||
"Content-Type: text/plain; charset=CHARSET\n" | |||
"Content-Transfer-Encoding: 8bit\n" | |||
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" | |||
#: qml/Main.qml:74 | |||
msgid "Connect to Bridge" | |||
msgstr "" | |||
#: qml/Main.qml:80 | |||
msgid "Searching for Hue bridges..." | |||
msgstr "" | |||
#: qml/Main.qml:89 | |||
msgid "Found a bridge. Press the button to pair." | |||
msgid_plural "Found %1 bridges. Press the button to pair." | |||
msgstr[0] "" | |||
msgstr[1] "" | |||
#: qml/Main.qml:95 | |||
msgid "Error: %1" | |||
msgstr "" | |||
#: qml/Main.qml:100 | |||
msgid "Connected." | |||
msgstr "" | |||
#: qml/Main.qml:211 | |||
msgid "Lights" | |||
msgstr "" | |||
#: qml/Main.qml:212 | |||
msgid "Groups" | |||
msgstr "" | |||
#: qml/Main.qml:338 | |||
msgid "Brightness" | |||
msgstr "" | |||
#: qml/Main.qml:366 | |||
msgid "Hue" | |||
msgstr "" | |||
#: qml/Main.qml:394 | |||
msgid "Saturation" | |||
msgstr "" |
@@ -12,6 +12,13 @@ ApplicationWindow { | |||
title: "Radiate" | |||
Settings { | |||
id: settings | |||
property string bridgeHost | |||
property string bridgeUsername | |||
} | |||
GTTranslator { id: tr } | |||
Component { | |||
id: headerBar | |||
ToolBar { | |||
@@ -39,12 +46,6 @@ ApplicationWindow { | |||
} | |||
} | |||
Settings { | |||
id: settings | |||
property string bridgeHost | |||
property string bridgeUsername | |||
} | |||
StackView { | |||
id: stackView | |||
anchors.fill: parent | |||
@@ -70,13 +71,13 @@ ApplicationWindow { | |||
Page { | |||
header: Loader { | |||
sourceComponent: headerBar | |||
property var title: "Connect to Bridge" | |||
property var title: tr.gettext("Connect to Bridge") | |||
} | |||
ColumnLayout { | |||
Label { | |||
id: statusText | |||
text: "Searching for Hue bridges..." | |||
text: tr.gettext("Searching for Hue bridges...") | |||
padding: 8 | |||
} | |||
} | |||
@@ -84,15 +85,19 @@ ApplicationWindow { | |||
PairingManager { | |||
id: connector | |||
onDiscovered_bridges: { | |||
statusText.text = "Found %1 bridge(s). Press the button to pair.".arg(count) | |||
statusText.text = tr.ngettext( | |||
"Found a bridge. Press the button to pair.", | |||
"Found %1 bridges. Press the button to pair.", | |||
count, | |||
).arg(count) | |||
} | |||
onErrored: { | |||
statusText.text = "Error: %1".arg(err) | |||
statusText.text = tr.gettext("Error: %1").arg(err) | |||
} | |||
onAuthenticated: { | |||
settings.bridgeHost = address; | |||
settings.bridgeUsername = username; | |||
statusText.text = "Connected."; | |||
statusText.text = tr.gettext("Connected."); | |||
stackView.replace(homePage) | |||
} | |||
} | |||
@@ -203,8 +208,8 @@ ApplicationWindow { | |||
currentIndex: tabView.currentIndex | |||
Layout.fillWidth: true | |||
TabButton { text: "Lights" } | |||
TabButton { text: "Groups" } | |||
TabButton { text: tr.gettext("Lights") } | |||
TabButton { text: tr.gettext("Groups") } | |||
} | |||
} | |||
@@ -330,7 +335,7 @@ ApplicationWindow { | |||
width: parent.width | |||
Item { Layout.preferredWidth: 8 } | |||
Label { | |||
text: "Brightness" | |||
text: tr.gettext("Brightness") | |||
verticalAlignment: Text.AlignVCenter | |||
} | |||
Item { Layout.fillWidth: true } | |||
@@ -358,7 +363,7 @@ ApplicationWindow { | |||
Layout.preferredWidth: 8 | |||
} | |||
Label { | |||
text: "Hue" | |||
text: tr.gettext("Hue") | |||
verticalAlignment: Text.AlignVCenter | |||
} | |||
Item { Layout.fillWidth: true } | |||
@@ -386,7 +391,7 @@ ApplicationWindow { | |||
Layout.preferredWidth: 8 | |||
} | |||
Label { | |||
text: "Saturation" | |||
text: tr.gettext("Saturation") | |||
verticalAlignment: Text.AlignVCenter | |||
} | |||
Item { Layout.fillWidth: true } | |||
@@ -1,54 +0,0 @@ | |||
/* Copyright (C) 2018 Olivier Goffart <ogoffart@woboq.com> | |||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and | |||
associated documentation files (the "Software"), to deal in the Software without restriction, | |||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, | |||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, | |||
subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in all copies or substantial | |||
portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT | |||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES | |||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||
*/ | |||
extern crate cpp_build; | |||
use std::process::Command; | |||
fn qmake_query(var: &str) -> String { | |||
String::from_utf8( | |||
Command::new("qmake") | |||
.args(&["-query", var]) | |||
.output() | |||
.expect("Failed to execute qmake. Make sure 'qmake' is in your path") | |||
.stdout, | |||
).expect("UTF-8 conversion failed") | |||
} | |||
fn main() { | |||
let qt_include_path = qmake_query("QT_INSTALL_HEADERS"); | |||
let qt_library_path = qmake_query("QT_INSTALL_LIBS"); | |||
cpp_build::Config::new() | |||
.include(qt_include_path.trim()) | |||
.build("src/main.rs"); | |||
let macos_lib_search = if cfg!(target_os = "macos") { | |||
"=framework" | |||
} else { | |||
"" | |||
}; | |||
let macos_lib_framework = if cfg!(target_os = "macos") { "" } else { "5" }; | |||
println!( | |||
"cargo:rustc-link-search{}={}", | |||
macos_lib_search, | |||
qt_library_path.trim() | |||
); | |||
println!("cargo:rustc-link-lib{}=Qt{}Widgets", macos_lib_search, macos_lib_framework); | |||
println!("cargo:rustc-link-lib{}=Qt{}Gui", macos_lib_search, macos_lib_framework); | |||
println!("cargo:rustc-link-lib{}=Qt{}Core", macos_lib_search, macos_lib_framework); | |||
println!("cargo:rustc-link-lib{}=Qt{}Quick", macos_lib_search, macos_lib_framework); | |||
println!("cargo:rustc-link-lib{}=Qt{}Qml", macos_lib_search, macos_lib_framework); | |||
println!("cargo:rustc-link-lib{}=Qt{}QuickControls2", macos_lib_search, macos_lib_framework); | |||
} |
@@ -16,6 +16,57 @@ mod connection_connector; | |||
use crate::connection_connector::BridgeConnectionConnector; | |||
use crate::pairing::PairingManager; | |||
mod translation_src { | |||
include!(concat!(env!("OUT_DIR"), "/mo.rs")); | |||
} | |||
lazy_static! { | |||
static ref TRANSLATIONS: gettext::Catalog = { | |||
let requested = std::env::var("LANG").ok().into_iter().map(|value| { | |||
let mut spl = value.split('.'); | |||
spl.next().unwrap().to_owned() | |||
}).collect::<Vec<_>>(); | |||
let available: Vec<String> = translation_src::MO_FILES.keys().cloned() | |||
.chain(vec!["en_US".to_owned()].into_iter()).collect(); | |||
println!("{:?} {:?}", requested, available); | |||
let negotiated = fluent_locale::negotiate_languages( | |||
&requested, | |||
&available, | |||
Some("en_US"), | |||
&fluent_locale::NegotiationStrategy::Lookup | |||
); | |||
println!("negotiated languages: {:?}", negotiated); | |||
let negotiated = if negotiated.len() > 0 { | |||
negotiated[0] | |||
} else { | |||
"en_US" | |||
}; | |||
if let Some(src) = translation_src::MO_FILES.get(negotiated) { | |||
gettext::Catalog::parse(*src).unwrap() | |||
} else { | |||
gettext::Catalog::parse(&include_bytes!("../empty.mo")[..]).unwrap() | |||
} | |||
}; | |||
} | |||
#[derive(QObject, Default)] | |||
struct GTTranslator { | |||
base: qt_base_class!(trait QObject), | |||
gettext: qt_method!(fn(&self, id: QString) -> QString), | |||
} | |||
impl GTTranslator { | |||
fn gettext<'a>(&self, msgid: QString) -> QString { | |||
let msgid: String = msgid.into(); | |||
println!("msgid: {}", msgid); | |||
TRANSLATIONS.gettext(&msgid).into() | |||
} | |||
} | |||
fn main() { | |||
tokio::run(futures::lazy(move || { | |||
unsafe { | |||
@@ -36,6 +87,7 @@ fn main() { | |||
let plugin_name = cstr!("RadiateApp"); | |||
qml_register_type::<PairingManager>(plugin_name, 0, 0, cstr!("PairingManager")); | |||
qml_register_type::<BridgeConnectionConnector>(plugin_name, 0, 0, cstr!("BridgeConnectionConnector")); | |||
qml_register_type::<GTTranslator>(plugin_name, 0, 0, cstr!("GTTranslator")); | |||
let mut engine = QmlEngine::new(); | |||
engine.load_file("qrc:/qml/Main.qml".into()); | |||
engine.exec(); | |||