diff --git a/go.mod b/go.mod index 3acfd98c..96f34001 100644 --- a/go.mod +++ b/go.mod @@ -10,4 +10,5 @@ require ( github.com/therecipe/qt v0.0.0-20200126204426-5074eb6d8c41 github.com/therecipe/qt/internal/binding/files/docs/5.12.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect github.com/therecipe/qt/internal/binding/files/docs/5.13.0 v0.0.0-20200126204426-5074eb6d8c41 // indirect + golang.org/x/crypto v0.0.0-20200320181102-891825fb96df ) diff --git a/go/constants/settings.go b/go/constants/settings.go index 6e148d14..1604b68e 100644 --- a/go/constants/settings.go +++ b/go/constants/settings.go @@ -1,5 +1,5 @@ package constants -const BlockUnknownPeersSetting = ".blockunknownpeers" -const LocaleSetting = ".locale" -const ZoomSetting = ".zoom" +const BlockUnknownPeersSetting = "blockunknownpeers" +const LocaleSetting = "locale" +const ZoomSetting = "zoom" diff --git a/go/handlers/peerHandler.go b/go/handlers/peerHandler.go index 537edc39..c6c8f02e 100644 --- a/go/handlers/peerHandler.go +++ b/go/handlers/peerHandler.go @@ -174,7 +174,6 @@ func upgradeSchema1(p peerC.CwtchPeer) { if zoom, exists := p.GetAttribute("settings.zoom"); exists { p.SetAttribute(attr.GetSettingsScope(constants.ZoomSetting), zoom) - } if blockunknown, exists := p.GetAttribute("settings.blockunknownpeers"); exists { diff --git a/go/the/globals.go b/go/the/globals.go index 10511e45..5ad7e9c2 100644 --- a/go/the/globals.go +++ b/go/the/globals.go @@ -4,6 +4,7 @@ import ( "cwtch.im/cwtch/app" "cwtch.im/cwtch/event" libPeer "cwtch.im/cwtch/peer" + "cwtch.im/cwtch/storage/v1" "git.openprivacy.ca/openprivacy/connectivity" ) @@ -18,3 +19,4 @@ var ACN connectivity.ACN var Peer libPeer.CwtchPeer var CwtchDir string var IPCBridge event.IPCBridge +var GlobalSettingsFile v1.FileStore diff --git a/go/ui/gcd.go b/go/ui/gcd.go index afabe59e..0910fc7a 100644 --- a/go/ui/gcd.go +++ b/go/ui/gcd.go @@ -20,11 +20,13 @@ import ( type GrandCentralDispatcher struct { core.QObject - QMLEngine *qml.QQmlApplicationEngine + QMLEngine *qml.QQmlApplicationEngine Translator, OpaqueTranslator *core.QTranslator uIManagers map[string]Manager // profile-onion : Manager + GlobalSettings *GlobalSettings + profileLock sync.Mutex conversationLock sync.Mutex @@ -32,7 +34,9 @@ type GrandCentralDispatcher struct { m_selectedConversation string _ string `property:"os"` - _ float32 `property:"themeScale"` + _ float32 `property:"themeScale,auto,changed"` + _ string `property:"theme,auto,changed` + _ string `property:"locale,auto,changed"` _ string `property:"version"` _ string `property:"buildDate"` _ string `property:"assetPath"` @@ -73,14 +77,13 @@ type GrandCentralDispatcher struct { // settings helpers _ func(str string) `signal:"InvokePopup"` - _ func(zoom, locale string, blockunknownpeers bool) `signal:"SupplySettings"` + _ func(locale string, zoom float32, theme string) `signal:"SupplySettings"` _ func(groupID, name, server, invitation string, accepted bool, addrbooknames, addrbookaddrs []string) `signal:"SupplyGroupSettings"` _ func(onion, nick string, blocked bool) `signal:"SupplyPeerSettings"` // signals emitted from the ui (written in go, below) // ui - _ func() `signal:"onActivate,auto"` - _ func(locale string) `signal:"setLocale,auto"` + _ func() `signal:"onActivate,auto"` // profile managemenet _ func(onion, nick string) `signal:"updateNick,auto"` _ func(handle string) `signal:"loadProfile,auto"` @@ -102,7 +105,6 @@ type GrandCentralDispatcher struct { _ func(groupID string) `signal:"leaveGroup,auto"` _ func(groupID string) `signal:"acceptGroup,auto"` _ func() `signal:"requestSettings,auto"` - _ func(zoom, locale string) `signal:"saveSettings,auto"` _ func(groupID string) `signal:"requestGroupSettings,auto"` _ func(groupID, nick string) `signal:"saveGroupSettings,auto"` _ func() `signal:"requestPeerSettings,auto"` @@ -117,6 +119,9 @@ type GrandCentralDispatcher struct { func (this *GrandCentralDispatcher) init() { this.uIManagers = make(map[string]Manager) + this.GlobalSettings = ReadGlobalSettings() + this.SetThemeScale(this.GlobalSettings.Zoom) + this.SetTheme(this.GlobalSettings.Theme) } // GetUiManager gets (and creates if required) a ui Manager for the supplied profile id @@ -323,31 +328,11 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { } func (this *GrandCentralDispatcher) requestSettings() { - zoom, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.ZoomSetting)) - if !exists { - zoom = "1.0" - } - locale, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.LocaleSetting)) - if !exists { - // TODO: pull env locale - locale = "" - } - - blockunkownpeers, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.BlockUnknownPeersSetting)) - if !exists { - blockunkownpeers = "false" - } - this.SupplySettings(zoom, locale, blockunkownpeers == "true") + this.SupplySettings(this.GlobalSettings.Locale, this.GlobalSettings.Zoom, this.GlobalSettings.Theme) } func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) { - // saveSettings accidentally gets called once when the app first starts but before the app has been prepared - // so let's just ignore that one - if the.CwtchApp == nil { - return - } - the.Peer.SetAttribute(attr.GetSettingsScope(constants.ZoomSetting), zoom) } func (this *GrandCentralDispatcher) requestPeerSettings() { @@ -360,6 +345,9 @@ func (this *GrandCentralDispatcher) requestPeerSettings() { name := getNick(contact.Onion) + // Todo: Move to profile settings + //blockunkownpeers, _ := the.Peer.GetAttribute(attr.GetPeerScope(constants.BlockUnknownPeersSetting)) + this.SupplyPeerSettings(contact.Onion, name, contact.Blocked) } @@ -558,15 +546,51 @@ func (this *GrandCentralDispatcher) allowUnknownPeers() { the.EventBus.Publish(event.NewEvent(event.AllowUnknownPeers, map[event.Field]string{})) } -func (this *GrandCentralDispatcher) setLocale(locale string) { - this.SetLocale_helper(locale) +func (this *GrandCentralDispatcher) localeChanged(locale string) { + this.GlobalSettings.Locale = locale + WriteGlobalSettings(this.GlobalSettings) + this.setLocaleHelper(locale) +} - the.Peer.SetAttribute(attr.GetSettingsScope(constants.LocaleSetting), locale) +func (this *GrandCentralDispatcher) setLocaleHelper(locale string) { + log.Debugf("Loading translators for '%v'\n", locale) + newTranslator := core.NewQTranslator(nil) + success := newTranslator.Load("translation_"+locale, ":/i18n/", "", "") + if success { + core.QCoreApplication_RemoveTranslator(this.Translator) + this.Translator = newTranslator + core.QCoreApplication_InstallTranslator(this.Translator) + } else { + log.Errorf("Could not load translator for '%v'\n", locale) + } - zoom, _ := the.Peer.GetAttribute(attr.GetSettingsScope(constants.ZoomSetting)) - blockunkownpeers, _ := the.Peer.GetAttribute(attr.GetPeerScope(constants.BlockUnknownPeersSetting)) - this.SupplySettings(zoom, locale, blockunkownpeers == "true") + newOpaqueTranslator := core.NewQTranslator(nil) + success = newOpaqueTranslator.Load("translation_"+locale, ":/qml/opaque/i18n/", "", "") + if success { + core.QCoreApplication_RemoveTranslator(this.OpaqueTranslator) + this.OpaqueTranslator = newOpaqueTranslator + core.QCoreApplication_InstallTranslator(this.OpaqueTranslator) + } else { + log.Errorf("Could not load opaque translator for '%v'\n", locale) + } + this.QMLEngine.Retranslate() +} + +func (this *GrandCentralDispatcher) themeScaleChanged(newThemeScale float32) { + // TODO: solve why themeScale > 2.0 halts app launch - related task - solve all the qml launch warnings around anchors/layouts + if this.Os() != "android" { + if newThemeScale > 1.99 { + this.SetThemeScale(1.99) + } + } + this.GlobalSettings.Zoom = newThemeScale + WriteGlobalSettings(this.GlobalSettings) +} + +func (this *GrandCentralDispatcher) themeChanged(newTheme string) { + this.GlobalSettings.Theme = newTheme + WriteGlobalSettings(this.GlobalSettings) } func (this *GrandCentralDispatcher) onActivate() { @@ -576,14 +600,6 @@ func (this *GrandCentralDispatcher) onActivate() { } } -func (this *GrandCentralDispatcher) SetLocale_helper(locale string) { - core.QCoreApplication_RemoveTranslator(this.Translator) - this.Translator = core.NewQTranslator(nil) - this.Translator.Load("translation_"+locale, ":/i18n/", "", "") - core.QCoreApplication_InstallTranslator(this.Translator) - this.QMLEngine.Retranslate() -} - func (this *GrandCentralDispatcher) unlockProfiles(password string) { the.CwtchApp.LoadProfiles(password) } @@ -618,13 +634,6 @@ func (this *GrandCentralDispatcher) loadProfile(onion string) { // Only join servers for active and explicitly accepted groups. this.GetUiManager(this.selectedProfile()).AddContact(groups[i]) } - - // load ui preferences - this.RequestSettings() - locale, exists := the.Peer.GetAttribute(attr.GetSettingsScope(constants.LocaleSetting)) - if exists { - this.SetLocale_helper(locale) - } } func (this *GrandCentralDispatcher) createProfile(nick string, defaultPass bool, password string) { diff --git a/go/ui/settings.go b/go/ui/settings.go new file mode 100644 index 00000000..1b4e4208 --- /dev/null +++ b/go/ui/settings.go @@ -0,0 +1,104 @@ +package ui + +import ( + "crypto/rand" + "cwtch.im/cwtch/storage/v1" + "cwtch.im/ui/go/the" + "encoding/json" + "git.openprivacy.ca/openprivacy/log" + "github.com/therecipe/qt/core" + "golang.org/x/crypto/pbkdf2" + "golang.org/x/crypto/sha3" + "io" + "io/ioutil" + "os" + "path" + "runtime" +) + +const GlobalSettingsFilename = "ui.globals" +const saltFile = "SALT" + +type GlobalSettings struct { + Zoom float32 + Locale string + Theme string +} + +var DefaultGlobalSettings = GlobalSettings{ + Zoom: 1.0, + Locale: "en", + Theme: "light", +} + +// createKeySalt derives a key from a password: returns key, salt, err +func createKeySalt(password string) ([32]byte, [128]byte, error) { + var salt [128]byte + if _, err := io.ReadFull(rand.Reader, salt[:]); err != nil { + log.Errorf("Cannot read from random: %v\n", err) + return [32]byte{}, salt, err + } + dk := pbkdf2.Key([]byte(password), salt[:], 4096, 32, sha3.New512) + + var dkr [32]byte + copy(dkr[:], dk) + return dkr, salt, nil +} + +func createKey(password string, salt []byte) [32]byte { + dk := pbkdf2.Key([]byte(password), salt, 4096, 32, sha3.New512) + + var dkr [32]byte + copy(dkr[:], dk) + return dkr +} + +func InitGlobalSettingsFile(directory string, password string) error { + var key [32]byte + salt, err := ioutil.ReadFile(path.Join(directory, saltFile)) + if err != nil { + var newSalt [128]byte + key, newSalt, err = createKeySalt(password) + if err != nil { + return err + } + os.Mkdir(directory, 0700) + err := ioutil.WriteFile(path.Join(directory, saltFile), newSalt[:], 0600) + if err != nil { + return err + } + } else { + key = createKey(password, salt) + } + + the.GlobalSettingsFile = v1.NewFileStore(directory, GlobalSettingsFilename, key) + return nil +} + +func ReadGlobalSettings() *GlobalSettings { + settings := DefaultGlobalSettings + if runtime.GOOS == "android" { + settings.Zoom = 2.9 + } + settings.Locale = core.QLocale_System().Name() + + settingsBytes, err := the.GlobalSettingsFile.Read() + if err != nil { + log.Errorf("Could not read global ui settings: %v\n", err) + return &settings + } + + err = json.Unmarshal(settingsBytes, &settings) + if err != nil { + log.Errorf("Could not parse global ui settings: %v\n", err) + } + return &settings +} + +func WriteGlobalSettings(globalSettings *GlobalSettings) { + bytes, _ := json.Marshal(globalSettings) + err := the.GlobalSettingsFile.Write(bytes) + if err != nil { + log.Errorf("Could not write global ui settings: %v\n", err) + } +} diff --git a/i18n/translation_de.qm b/i18n/translation_de.qm index 135d603d..c442202f 100644 Binary files a/i18n/translation_de.qm and b/i18n/translation_de.qm differ diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index af483be3..b843b25d 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -227,38 +227,38 @@ PeerSettingsPane - + address-label Adresse - + copy-btn Kopieren - + copied-to-clipboard-notification notification: copied to clipboard in die Zwischenablage kopiert - + display-name-label Angezeigter Name - + save-btn speichern - + delete-btn löschen - + block-btn @@ -410,36 +410,110 @@ Cwtch Einstellungen - zoom-label Interface zoom (mostly affects text and button sizes) - Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen) + Benutzeroberflächen-Zoom (betriftt hauptsächlich Text- und Knopgrößen) - + + setting-language + Language + + + + + locale-en + + + + + locale-fr + + + + + locale-pt + + + + + locale-de + + + + + setting-interface-zoom + Interface Zoom + + + + large-text-label Groß - - default-scaling-text - "Default size text (scale factor: " - defaultmäßige Textgröße (Skalierungsfaktor: - - - - small-text-label - Klein - - - - version %1 builddate %2 - Version: %1 Built on: %2 + + setting-theme + Theme - - block-unknown-label + + theme-light + + + + + theme-dark + + + + + version %1 + Version %1 + + + + + builddate %2 + Built on: %2 + + + + default-scaling-text + "Default size text (scale factor: " + defaultmäßige Textgröße (Skalierungsfaktor: + + + + small-text-label + Klein + + + + Statusbar + + + network-status-disconnected + Disconnected from the internet, check your connection + + + + + network-status-attempting-tor + Attempting to connect to Tor network + + + + + network-status-connecting + Connecting... + + + + + network-status-online + Online diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index fb3618ba..49063bb7 100644 Binary files a/i18n/translation_en.qm and b/i18n/translation_en.qm differ diff --git a/i18n/translation_en.ts b/i18n/translation_en.ts index bce3a55a..91af2302 100644 --- a/i18n/translation_en.ts +++ b/i18n/translation_en.ts @@ -322,33 +322,33 @@ Right-click to reset. PeerSettingsPane - + address-label Address - + copy-btn Copy - + copied-to-clipboard-notification notification: copied to clipboard Copied to Clipboard - + display-name-label Display Name - + save-btn Save - + block-btn Block Peer @@ -357,7 +357,7 @@ Right-click to reset. Unblock Peer - + delete-btn Delete @@ -517,35 +517,91 @@ Right-click to reset. Cwtch Settings - version %1 builddate %2 Version: %1 Built on: %2 - Version: %1 Built on: %2 + Version: %1 Built on: %2 - zoom-label Interface zoom (mostly affects text and button sizes) - Interface zoom (mostly affects text and button sizes) + Interface zoom (mostly affects text and button sizes) - block-unknown-label - Block Unknown Peers + Block Unknown Peers - + + setting-language + Language + Language + + + + locale-en + English + + + + locale-fr + Frances + + + + locale-pt + Portuguesa + + + + locale-de + Deutsche + + + + setting-interface-zoom + Interface Zoom + Zoom level + + + large-text-label Large - - default-scaling-text - "Default size text (scale factor: " - Default size text (scale factor: + + setting-theme + Theme + Theme - + + theme-light + Light + + + + theme-dark + Dark + + + + version %1 + Version %1 + Version %1 + + + + builddate %2 + Built on: %2 + Built on: %2 + + + default-scaling-text + "Default size text (scale factor: " + Default size text (scale factor: + + + small-text-label Small @@ -561,24 +617,28 @@ Right-click to reset. Statusbar + network-status-disconnected Disconnected from the internet, check your connection - Disconnected from the internet, check your connection + Disconnected from the internet, check your connection + network-status-attempting-tor Attempting to connect to Tor network - Attempting to connect to Tor network + Attempting to connect to Tor network + network-status-connecting Connecting... - Connecting to network and peers... + Connecting to network and peers... + network-status-online Online - Online + Online diff --git a/i18n/translation_fr.qm b/i18n/translation_fr.qm index 7b55a58d..3026fc59 100644 Binary files a/i18n/translation_fr.qm and b/i18n/translation_fr.qm differ diff --git a/i18n/translation_fr.ts b/i18n/translation_fr.ts index 6453d504..0193e6f0 100644 --- a/i18n/translation_fr.ts +++ b/i18n/translation_fr.ts @@ -227,38 +227,38 @@ PeerSettingsPane - + address-label Adresse - + copy-btn Copier - + copied-to-clipboard-notification notification: copied to clipboard Copié dans le presse-papier - + display-name-label Pseudo - + save-btn Sauvegarder - + delete-btn Effacer - + block-btn @@ -410,36 +410,110 @@ Préférences Cwtch - zoom-label Interface zoom (mostly affects text and button sizes) - Interface zoom (essentiellement la taille du texte et des composants de l'interface) + Interface zoom (essentiellement la taille du texte et des composants de l'interface) - + + setting-language + Language + + + + + locale-en + + + + + locale-fr + + + + + locale-pt + + + + + locale-de + + + + + setting-interface-zoom + Interface Zoom + + + + large-text-label Large - - default-scaling-text - "Default size text (scale factor: " - Taille par défaut du texte (échelle: - - - - small-text-label - Petit - - - - version %1 builddate %2 - Version: %1 Built on: %2 + + setting-theme + Theme - - block-unknown-label + + theme-light + + + + + theme-dark + + + + + version %1 + Version %1 + + + + + builddate %2 + Built on: %2 + + + + default-scaling-text + "Default size text (scale factor: " + Taille par défaut du texte (échelle: + + + + small-text-label + Petit + + + + Statusbar + + + network-status-disconnected + Disconnected from the internet, check your connection + + + + + network-status-attempting-tor + Attempting to connect to Tor network + + + + + network-status-connecting + Connecting... + + + + + network-status-online + Online diff --git a/i18n/translation_pt.qm b/i18n/translation_pt.qm index a1155b51..39f9c4bd 100644 Binary files a/i18n/translation_pt.qm and b/i18n/translation_pt.qm differ diff --git a/i18n/translation_pt.ts b/i18n/translation_pt.ts index 2c823d90..4bcda35a 100644 --- a/i18n/translation_pt.ts +++ b/i18n/translation_pt.ts @@ -227,38 +227,38 @@ PeerSettingsPane - + address-label Endereço - + copy-btn Copiar - + copied-to-clipboard-notification notification: copied to clipboard Copiado - + display-name-label Nome de Exibição - + save-btn Salvar - + delete-btn Deletar - + block-btn @@ -410,36 +410,110 @@ Configurações do Cwtch - zoom-label Interface zoom (mostly affects text and button sizes) - Zoom da interface (afeta principalmente tamanho de texto e botões) + Zoom da interface (afeta principalmente tamanho de texto e botões) - + + setting-language + Language + + + + + locale-en + + + + + locale-fr + + + + + locale-pt + + + + + locale-de + + + + + setting-interface-zoom + Interface Zoom + + + + large-text-label Grande - - default-scaling-text - "Default size text (scale factor: " - Texto tamanho padrão (fator de escala: - - - - small-text-label - Pequeno - - - - version %1 builddate %2 - Version: %1 Built on: %2 + + setting-theme + Theme - - block-unknown-label + + theme-light + + + + + theme-dark + + + + + version %1 + Version %1 + + + + + builddate %2 + Built on: %2 + + + + default-scaling-text + "Default size text (scale factor: " + Texto tamanho padrão (fator de escala: + + + + small-text-label + Pequeno + + + + Statusbar + + + network-status-disconnected + Disconnected from the internet, check your connection + + + + + network-status-attempting-tor + Attempting to connect to Tor network + + + + + network-status-connecting + Connecting... + + + + + network-status-online + Online diff --git a/main.go b/main.go index f506b647..b2b987a7 100644 --- a/main.go +++ b/main.go @@ -131,6 +131,11 @@ func mainUi(flagLocal bool, flagClientUI bool) { app := gui.NewQGuiApplication(len(os.Args), os.Args) // our globals + err := ui.InitGlobalSettingsFile(path.Join(the.CwtchDir, "global"), the.AppPassword) + if err != nil { + log.Errorf("Could not access global ui config: %v\n", err) + os.Exit(-1) + } gcd := ui.NewGrandCentralDispatcher(nil) gcd.SetOs(runtime.GOOS) dir := core.QCoreApplication_ApplicationDirPath() @@ -154,9 +159,6 @@ func mainUi(flagLocal bool, flagClientUI bool) { gcd.SetBuildDate("now") } - //TODO: put theme stuff somewhere better - gcd.SetThemeScale(1.0) - // this is to load local qml files quickly when developing var qmlSource *core.QUrl if flagLocal { @@ -175,17 +177,11 @@ func mainUi(flagLocal bool, flagClientUI bool) { opaqueTranslator.Load("translation_en", ":/qml/opaque/i18n/", "", "") core.QCoreApplication_InstallTranslator(opaqueTranslator) - gcd.Translator = core.NewQTranslator(nil) - gcd.Translator.Load("translation_"+core.QLocale_System().Name(), ":/i18n/", "", "") - core.QCoreApplication_InstallTranslator(gcd.Translator) - gcd.OpaqueTranslator = core.NewQTranslator(nil) - gcd.OpaqueTranslator.Load("translation_"+core.QLocale_System().Name(), ":/qml/opaque/i18n/", "", "") - core.QCoreApplication_InstallTranslator(gcd.OpaqueTranslator) - core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true) quickcontrols2.QQuickStyle_SetStyle("Universe") engine := qml.NewQQmlApplicationEngine(nil) gcd.QMLEngine = engine + gcd.SetLocale(gcd.GlobalSettings.Locale) // prevent qt from initiating network connections (possible deanon attempts!) factory := qml.NewQQmlNetworkAccessManagerFactory() @@ -199,12 +195,6 @@ func mainUi(flagLocal bool, flagClientUI bool) { }) engine.SetNetworkAccessManagerFactory(factory) - // variables we want to access from inside qml - if runtime.GOOS == "android" { - gcd.SetThemeScale(2.9) - } else { - gcd.SetThemeScale(1.0) - } engine.RootContext().SetContextProperty("gcd", gcd) var androidCwtchActivity = android.NewCwtchActivity(nil) diff --git a/qml.qrc b/qml.qrc index d8bacb3c..794ee803 100644 --- a/qml.qrc +++ b/qml.qrc @@ -11,14 +11,15 @@ qml/panes/PeerSettingsPane.qml qml/panes/SettingsPane.qml qml/panes/SplashPane.qml - qml/panes/ProfileManagerPane.qml - qml/panes/ProfileAddEditPane.qml + qml/panes/ProfileManagerPane.qml + qml/panes/ProfileAddEditPane.qml qml/widgets/ContactList.qml qml/widgets/ContactRow.qml qml/widgets/Message.qml qml/widgets/MyProfile.qml qml/widgets/ProfileList.qml qml/widgets/ProfileRow.qml + qml/widgets/Statusbar.qml i18n/translation_de.qm i18n/translation_en.qm i18n/translation_fr.qm diff --git a/qml/main.qml b/qml/main.qml index 0fe47487..845ff519 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -81,6 +81,12 @@ ApplicationWindow { Toolbar { id: toolbar + + onLeftMenu: { + gcd.requestSettings() + parentStack.pane = parentStack.settingsPane + } + onBack: { backFn() } onRightMenu: { @@ -104,16 +110,16 @@ ApplicationWindow { readonly property int splashPane: 0 readonly property int managementPane: 1 - readonly property int addEditProfilePane: 2 - readonly property int profilePane: 3 + readonly property int settingsPane: 2 + readonly property int addEditProfilePane: 3 + readonly property int profilePane: 4 property alias pane: parentStack.currentIndex Rectangle { // Splash pane color: Theme.backgroundMainColor - //Layout.fillHeight: true - //Layout.minimumWidth: Layout.maximumWidth - //Layout.minimumHeight: parent.height - anchors.fill: parent + Layout.fillHeight: true + Layout.fillWidth: true + //anchors.fill: parent visible: true @@ -125,7 +131,9 @@ ApplicationWindow { } Rectangle { // Profile login/management pane - anchors.fill: parent + Layout.fillHeight: true + Layout.fillWidth: true + //anchors.fill: parent visible: false color: Theme.backgroundMainColor @@ -135,8 +143,23 @@ ApplicationWindow { } } - Rectangle { // Profile login/management pane - anchors.fill: parent + Rectangle { // Settings pane + Layout.fillHeight: true + Layout.fillWidth: true + //anchors.fill: parent + color: Theme.backgroundMainColor + + SettingsPane { + id: settingsPane + anchors.fill: parent + } + } + + + Rectangle { // Profile Add / Edit pane + Layout.fillHeight: true + Layout.fillWidth: true + //anchors.fill: parent color: Theme.backgroundMainColor @@ -148,7 +171,9 @@ ApplicationWindow { RowLayout { // CONTAINS EVERYTHING EXCEPT THE TOOLBAR - anchors.fill: parent + Layout.fillHeight: true + Layout.fillWidth: true + //anchors.fill: parent spacing: 0 Rectangle { // THE LEFT PANE WITH TOOLS AND CONTACTS @@ -179,10 +204,9 @@ ApplicationWindow { property alias pane: theStack.currentIndex readonly property int emptyPane: 0 readonly property int messagePane: 1 - readonly property int settingsPane: 2 - readonly property int userProfilePane: 3 - readonly property int groupProfilePane: 4 - readonly property int addGroupPane: 5 + readonly property int userProfilePane: 2 + readonly property int groupProfilePane: 3 + readonly property int addGroupPane: 4 Item { anchors.fill: parent } // empty @@ -190,7 +214,6 @@ ApplicationWindow { anchors.fill: parent } - SettingsPane { anchors.fill: parent } PeerSettingsPane { anchors.fill: parent } @@ -239,17 +262,20 @@ ApplicationWindow { } else { toolbar.leftMenuVisible = false toolbar.backVisible = true - - if (currentIndex == addEditProfilePane) { - toolbar.color = Theme.backgroundPaneColor - } else if (currentIndex == profilePane) { - toolbar.color = Theme.backgroundPaneColor - } + toolbar.color = Theme.backgroundPaneColor } } } Component.onCompleted: updateToolbar() + + Connections { + target: Theme + + onThemeChanged: { + parentStack.updateToolbar() + } + } } Statusbar { diff --git a/qml/opaque b/qml/opaque index db305895..0ec6a2df 160000 --- a/qml/opaque +++ b/qml/opaque @@ -1 +1 @@ -Subproject commit db305895e70fbb599d2600a7e1f2245a56380217 +Subproject commit 0ec6a2df571e1a9c462d902cfc89f2402a9bef97 diff --git a/qml/panes/PeerSettingsPane.qml b/qml/panes/PeerSettingsPane.qml index d9f39dbf..b6b9904c 100644 --- a/qml/panes/PeerSettingsPane.qml +++ b/qml/panes/PeerSettingsPane.qml @@ -11,131 +11,93 @@ import "../opaque" as Opaque import "../opaque/styles" import "../opaque/theme" -ColumnLayout { // peerSettingsPane +Opaque.SettingsList { // settingsPane id: root anchors.fill: parent + property bool blocked - Flickable { + settings: Column { anchors.fill: parent - boundsBehavior: Flickable.StopAtBounds - clip:true - contentWidth: tehcol.width - contentHeight: tehcol.height + + Opaque.Setting { + inline: false + label: qsTr("address-label") + + field: Opaque.ButtonTextField { + id: txtOnion + readOnly: true + button_text: qsTr("copy-btn") + dropShadowColor: Theme.dropShadowPaneColor + onClicked: { + //: notification: copied to clipboard + gcd.popup(qsTr("copied-to-clipboard-notification")) + txtOnion.selectAll() + txtOnion.copy() + } + } + } + + + + Opaque.Setting { + inline: false + + label: qsTr("display-name-label") + + field: Opaque.ButtonTextField { + id: txtDisplayName + button_text: qsTr("save-btn") + dropShadowColor: Theme.dropShadowPaneColor + onClicked: { + gcd.savePeerSettings(txtOnion.text, txtDisplayName.text) + // TODO: broken + theStack.title = txtDisplayName.text + theStack.pane = theStack.messagePane + } + } + } + + + + + Opaque.Setting { + label: qsTr("block-btn") + + + field: Opaque.ToggleSwitch { + anchors.right: parent.right + + isToggled: root.blocked // ? qsTr("unblock-btn") : qsTr("block-btn") + onToggled: function() { + if (root.blocked) { + gcd.unblockPeer(txtOnion.text) + } else { + gcd.blockPeer(txtOnion.text) + } + root.blocked = !root.blocked + isToggled = root.blocked + } + } + } Column { - id: tehcol - width: root.width - padding:20 - spacing: 10 - GridLayout { - columns: 1 - width:parent.width * 0.95 - anchors.horizontalCenter: parent.horizontalCenter - Opaque.EllipsisLabel { - anchors.left:parent.left - color: Theme.mainTextColor - text: qsTr("address-label") - font.styleName: "ExtraBold" - font.pointSize: 15 * gcd.themeScale - } + width:parent.width * 0.95 + anchors.horizontalCenter: parent.horizontalCenter + Opaque.Button { + icon: "regular/trash-alt" + text: qsTr("delete-btn") + anchors.right: parent.right - Opaque.ButtonTextField { - id: txtOnion - anchors.left:parent.left - anchors.right:parent.right - readOnly: true - button_text: qsTr("copy-btn") - dropShadowColor: Theme.dropShadowPaneColor - onClicked: { - //: notification: copied to clipboard - gcd.popup(qsTr("copied-to-clipboard-notification")) - txtOnion.selectAll() - txtOnion.copy() - } + + onClicked: { + gcd.deleteContact(txtOnion.text) + theStack.pane = theStack.emptyPane } } + } - Opaque.HLine{} - - GridLayout { - columns: 1 - width:parent.width * 0.95 - anchors.horizontalCenter: parent.horizontalCenter - Opaque.EllipsisLabel { - anchors.left:parent.left - color: Theme.mainTextColor - text: qsTr("display-name-label") - font.styleName: "ExtraBold" - font.pointSize: 15 * gcd.themeScale - } - - - Opaque.ButtonTextField { - id: txtDisplayName - anchors.left:parent.left - anchors.right:parent.right - button_text: qsTr("save-btn") - dropShadowColor: Theme.dropShadowPaneColor - onClicked: { - gcd.savePeerSettings(txtOnion.text, txtDisplayName.text) - theStack.title = txtDisplayName.text - theStack.pane = theStack.messagePane - } - } - } - - - Opaque.HLine{} - - - GridLayout { - columns: 2 - width:parent.width * 0.95 - anchors.horizontalCenter: parent.horizontalCenter - Opaque.EllipsisLabel { - color: Theme.mainTextColor - text: qsTr("block-btn") - font.styleName: "ExtraBold" - font.pointSize: 15 * gcd.themeScale - } - - Opaque.ToggleSwitch { - isToggled: root.blocked // ? qsTr("unblock-btn") : qsTr("block-btn") - anchors.right: parent.right - onToggled: function() { - if (root.blocked) { - gcd.unblockPeer(txtOnion.text) - } else { - gcd.blockPeer(txtOnion.text) - } - root.blocked = !root.blocked - isToggled = root.blocked - } - } - - - } - - Opaque.HLine{} - Column { - width:parent.width * 0.95 - anchors.horizontalCenter: parent.horizontalCenter - Opaque.Button { - icon: "regular/trash-alt" - text: qsTr("delete-btn") - anchors.right: parent.right - - - onClicked: { - gcd.deleteContact(txtOnion.text) - theStack.pane = theStack.emptyPane - } - } - } - - }//end of column with padding - }//end of flickable + } Connections { diff --git a/qml/panes/SettingsPane.qml b/qml/panes/SettingsPane.qml index 91f48e2d..848f92ba 100644 --- a/qml/panes/SettingsPane.qml +++ b/qml/panes/SettingsPane.qml @@ -9,130 +9,192 @@ import QtQuick.Controls.Styles 1.4 import "../opaque" as Opaque import "../opaque/controls" +import "../opaque/theme" -ColumnLayout { // settingsPane +Opaque.SettingsList { // settingsPane id: root anchors.fill: parent - Flickable { - anchors.fill: parent - boundsBehavior: Flickable.StopAtBounds - clip:true - contentWidth: tehcol.width - contentHeight: tehcol.height + settings: Column { + anchors.horizontalCenter: parent.horizontalCenter + width: 700 + Opaque.Setting { + //: Language + label: qsTr("setting-language") - Column { - id: tehcol - leftPadding: 10 - spacing: 5 - width: root.width + field: ComboBox { + id: cbLanguage + anchors.right: parent.right + anchors.left: parent.left - Opaque.ScalingLabel { - width: parent.width - wrapMode: TextEdit.Wrap - //: Version: %1 Built on: %2 - text: qsTr("version %1 builddate %2").arg(gcd.version).arg(gcd.buildDate) - } + property bool inited: false - Opaque.ScalingLabel { - width: parent.width - wrapMode: TextEdit.Wrap - //: Interface zoom (mostly affects text and button sizes) - text: qsTr("zoom-label") + ":" - } - - Slider { - id: zoomSlider - minimumValue: 0.5 - maximumValue: 4.0 - value: gcd.themeScale - updateValueWhileDragging: false - onValueChanged: { - gcd.themeScale = zoomSlider.value - saveSettings() + model: ListModel { + id: cbLangItems + ListElement { text: qsTr("locale-en"); value: "en" } + ListElement { text: qsTr("locale-fr"); value: "fr" } + ListElement { text: qsTr("locale-pt"); value: "pt" } + ListElement { text: qsTr("locale-de"); value: "de" } } - width: 400 - } - - CheckBox { - id: blockUnknownToggle - checked: true - onClicked: { - if (blockUnknownToggle.checked) { - gcd.blockUnknownPeers() - } else { - gcd.allowUnknownPeers() + onCurrentIndexChanged: { + var item = cbLangItems.get(cbLanguage.currentIndex) + // Comboboxes seem to fire one Change on load... + if (!cbLanguage.inited) { + cbLanguage.inited = true + return } + gcd.locale = item["value"] } - style: CheckBoxStyle { - label: Opaque.ScalingLabel { - text: qsTr("block-unknown-label") + + } + } + + Opaque.Setting { + //: Interface Zoom + label: qsTr("setting-interface-zoom") + + field: Row { + spacing: 10 + anchors.verticalCenter: parent.verticalCenter + + Opaque.ScalingLabel { + text: qsTr("small-text-label") + size: 8 + } + + Slider { + id: zoomSlider + minimumValue: 0.5 + // TODO: find out why > 2.0 halts desktop app on load - task: fix all the qml anchor/layout warnings on load + maximumValue: gcd.os == "android" ? 4.0 : 1.9 + value: gcd.themeScale + updateValueWhileDragging: false + onValueChanged: { + gcd.themeScale = zoomSlider.value } - } - } - - Opaque.ScalingLabel { - wrapMode: TextEdit.Wrap - text: qsTr("large-text-label") - size: 20 - } - - Opaque.ScalingLabel{ - width: parent.width - wrapMode: TextEdit.Wrap - //: "Default size text (scale factor: " - text: qsTr("default-scaling-text") + " " + Math.round(zoomSlider.value * 100) / 100 + ")" - } - - Opaque.ScalingLabel { - text: qsTr("small-text-label") - size: 8 - } - - GridLayout { - columns: 2 - columnSpacing: 10 - - - FlagButton { - emoji: "1f1e9-1f1ea" - locale: "de" + width: 200 } - FlagButton { - emoji: "1f1e8-1f1e6" - locale: "en" - selected: true - } - FlagButton { - locale: "fr" - emoji: "1f1eb-1f1f7" - } - - FlagButton { - locale: "pt" - emoji: "1f1e7-1f1f7" + Opaque.ScalingLabel { + anchors.verticalCenter: parent.verticalCenter + wrapMode: TextEdit.Wrap + text: qsTr("large-text-label") + size: 20 } } + } - }//end of column with padding - }//end of flickable + Opaque.Setting { + //: Theme + label: qsTr("setting-theme") + + field: ComboBox { + id: cbTheme + property bool inited: false + anchors.right: parent.right + anchors.left: parent.left + model: ListModel { + id: cbThemeItems + ListElement { text: qsTr("theme-light"); value: 'light' } + ListElement { text: qsTr("theme-dark"); value: 'dark' } + } + + onCurrentIndexChanged: { + var item = cbThemeItems.get(cbTheme.currentIndex) + // Comboboxes seem to fire one Change on load... + if (!cbTheme.inited) { + cbTheme.inited = true + return + } + gcd.theme = item["value"] + } + } + } - function saveSettings() { - // language switcher saves itself because erinn is a bad (read: amazing) programmer - gcd.saveSettings(zoomSlider.value, "") } + Connections { target: gcd - onSupplySettings: function(zoom, locale, blockunknown) { + onSupplySettings: function(locale, zoom, theme) { if (zoom != "") zoomSlider.value = zoom - // (locale is handled automatically by FlagButton) - blockUnknownToggle.checked = blockunknown + + for (var i=0; i < cbLangItems.count; i++) { + var item = cbLangItems.get(i) + if (item["value"] == locale) { + cbLanguage.currentIndex = i + break + } + } + + for (var i=0; i < cbThemeItems.count; i++) { + var item = cbThemeItems.get(i) + if (item["value"] == theme) { + cbTheme.currentIndex = i + break + } + } } } + + + + Opaque.ScalingLabel { + id: versionLabel + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: builddateLabel.top + //: Version %1 + text: qsTr("version %1").arg(gcd.version) + } + + Opaque.ScalingLabel { + id: builddateLabel + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + //: Built on: %2 + text: qsTr("builddate %2").arg(gcd.buildDate) + } + + + //end of flickable } + + +/* Opaque.ScalingLabel { +width: parent.width +wrapMode: TextEdit.Wrap +//: Interface zoom (mostly affects text and button sizes) +text: qsTr("zoom-label") + ":" +} + + + + +CheckBox { +id: blockUnknownToggle +checked: true +onClicked: { +if (blockUnknownToggle.checked) { +gcd.blockUnknownPeers() +} else { +gcd.allowUnknownPeers() +} +} +style: CheckBoxStyle { +label: Opaque.ScalingLabel { +text: qsTr("block-unknown-label") +} +} +} + + + + +*/ + + diff --git a/qml/widgets/ContactList.qml b/qml/widgets/ContactList.qml index 6420d707..f2cf8fdf 100644 --- a/qml/widgets/ContactList.qml +++ b/qml/widgets/ContactList.qml @@ -28,28 +28,28 @@ ColumnLayout { Opaque.IconTextField { - id: searchAddText - anchors.horizontalCenter: parent.horizontalCenter + id: searchAddText + anchors.horizontalCenter: parent.horizontalCenter - Layout.minimumWidth: parent.width - 60 - Layout.maximumWidth: parent.width - 60 + Layout.minimumWidth: parent.width - 60 + Layout.maximumWidth: parent.width - 60 - //: ex: "... paste an address here to add a contact ..." - placeholderText: qsTr("paste-address-to-add-contact") - horizontalAlignment: TextInput.AlignHCenter - icon: gcd.assetPath + "core/search-24px.svg" + //: ex: "... paste an address here to add a contact ..." + placeholderText: qsTr("paste-address-to-add-contact") + horizontalAlignment: TextInput.AlignHCenter + icon: gcd.assetPath + "core/search-24px.svg" - onTextChanged: { - if (text != "") { - gcd.importString(text) - text = "" - } + onTextChanged: { + if (text != "") { + gcd.importString(text) + text = "" } } + } Flickable { // THE ACTUAL CONTACT LIST diff --git a/qml/widgets/MyProfile.qml b/qml/widgets/MyProfile.qml index 8a9b8898..70c6269b 100644 --- a/qml/widgets/MyProfile.qml +++ b/qml/widgets/MyProfile.qml @@ -108,11 +108,8 @@ Item { Opaque.EllipsisLabel { id: name - anchors.right: undefined - anchors.left: undefined - color: Theme.portraitOnlineTextColor - pixelSize: Theme.usernameSize * gcd.themeScale + size: Theme.usernameSize * gcd.themeScale weight: Font.Bold text: nick extraPadding: addBtn.width + 30 diff --git a/qml/widgets/Statusbar.qml b/qml/widgets/Statusbar.qml index e1dfa0c0..8c8bc747 100644 --- a/qml/widgets/Statusbar.qml +++ b/qml/widgets/Statusbar.qml @@ -158,18 +158,18 @@ Rectangle { } function show() { - if (isHover || status != statusOnline) { - hideAnim.stop() - showAnim.start() - } - } + if (isHover || status != statusOnline) { + hideAnim.stop() + showAnim.start() + } + } - function hide() { - if (!isHover && status == statusOnline) { - showAnim.stop() - hideAnim.start() - } - } + function hide() { + if (!isHover && status == statusOnline) { + showAnim.stop() + hideAnim.start() + } + } onStatusChanged: { changeStatus() } @@ -183,4 +183,12 @@ Rectangle { } } + Connections { + target: Theme + + onThemeChanged: { + changeStatus() + } + } + }