diff options
Diffstat (limited to 'cmd/mist/assets/qml/main.qml')
-rw-r--r-- | cmd/mist/assets/qml/main.qml | 1095 |
1 files changed, 1095 insertions, 0 deletions
diff --git a/cmd/mist/assets/qml/main.qml b/cmd/mist/assets/qml/main.qml index 45d56e795..8f9a2d3cc 100644 --- a/cmd/mist/assets/qml/main.qml +++ b/cmd/mist/assets/qml/main.qml @@ -9,7 +9,1101 @@ import Ethereum 1.0 import "../ext/filter.js" as Eth import "../ext/http.js" as Http + ApplicationWindow { +<<<<<<< HEAD + id: root + + //flags: Qt.FramelessWindowHint + // Use this to make the window frameless. But then you'll need to do move and resize by hand + + property var ethx : Eth.ethx + property var catalog; + + width: 1200 + height: 820 + minimumHeight: 600 + minimumWidth: 800 + + title: "Mist" + + TextField { + id: copyElementHax + visible: false + } + + function copyToClipboard(text) { + copyElementHax.text = text + copyElementHax.selectAll() + copyElementHax.copy() + } + + // Takes care of loading all default plugins + Component.onCompleted: { + + catalog = addPlugin("./views/catalog.qml", {noAdd: true, close: false, section: "begin"}); + var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + + addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true}); + addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"}); + addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"}); + + mainSplit.setView(catalog.view, catalog.menuItem); + + //newBrowserTab("http://ethereum-dapp-catalog.meteor.com"); + + // Command setup + gui.sendCommand(0) + } + + function activeView(view, menuItem) { + mainSplit.setView(view, menuItem) + if (view.hideUrl) { + urlPane.visible = false; + mainView.anchors.top = rootView.top + } else { + urlPane.visible = true; + mainView.anchors.top = divider.bottom + } + } + + function addViews(view, path, options) { + var views = mainSplit.addComponent(view, options) + views.menuItem.path = path + + mainSplit.views.push(views); + + if(!options.noAdd) { + gui.addPlugin(path) + } + + return views + } + + function addPlugin(path, options) { + try { + if(typeof(path) === "string" && /^https?/.test(path)) { + console.log('load http') + Http.request(path, function(o) { + if(o.status === 200) { + var view = Qt.createQmlObject(o.responseText, mainView, path) + addViews(view, path, options) + } + }) + + return + } + + var component = Qt.createComponent(path); + if(component.status != Component.Ready) { + if(component.status == Component.Error) { + ethx.note("error: ", component.errorString()); + } + + return + } + + var view = mainView.createView(component, options) + var views = addViews(view, path, options) + + return views + } catch(e) { + console.log(e) + } + } + + function newBrowserTab(url) { + + var urlMatches = url.toString().match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i); + var requestedDomain = urlMatches && urlMatches[1]; + + var domainAlreadyOpen = false; + + console.log("requested: " + requestedDomain ) + + for(var i = 0; i < mainSplit.views.length; i++) { + if (mainSplit.views[i].view.url) { + var matches = mainSplit.views[i].view.url.toString().match(/^[a-z]*\:\/\/(?:www\.)?([^\/?#]+)(?:[\/?#]|$)/i); + var existingDomain = matches && matches[1]; + console.log("exists: " + existingDomain); + if (requestedDomain == existingDomain) { + domainAlreadyOpen = true; + mainSplit.views[i].view.url = url; + activeView(mainSplit.views[i].view, mainSplit.views[i].menuItem); + } + } + } + + if (!domainAlreadyOpen) { + var window = addPlugin("./views/browser.qml", {noAdd: true, close: true, section: "apps", active: true}); + window.view.url = url; + window.menuItem.title = "Mist"; + activeView(window.view, window.menuItem); + } + } + + + + menuBar: MenuBar { + Menu { + title: "File" + MenuItem { + text: "Import App" + shortcut: "Ctrl+o" + onTriggered: { + generalFileDialog.show(true, importApp) + } + } + + MenuItem { + text: "Add plugin" + onTriggered: { + generalFileDialog.show(true, function(path) { + addPlugin(path, {close: true, section: "apps"}) + }) + } + } + + MenuItem { + text: "New tab" + shortcut: "Ctrl+t" + onTriggered: { + activeView(catalog.view, catalog.menuItem); + } + } + + MenuSeparator {} + + MenuItem { + text: "Import key" + shortcut: "Ctrl+i" + onTriggered: { + generalFileDialog.show(true, function(path) { + gui.importKey(path) + }) + } + } + + MenuItem { + text: "Export keys" + shortcut: "Ctrl+e" + onTriggered: { + generalFileDialog.show(false, function(path) { + }) + } + } + + } + + Menu { + title: "Developer" + MenuItem { + iconSource: "../icecream.png" + text: "Debugger" + shortcut: "Ctrl+d" + onTriggered: eth.startDebugger() + } + + MenuItem { + text: "Import Tx" + onTriggered: { + txImportDialog.visible = true + } + } + + MenuItem { + text: "Run JS file" + onTriggered: { + generalFileDialog.show(true, function(path) { + eth.evalJavascriptFile(path) + }) + } + } + + MenuItem { + text: "Dump state" + onTriggered: { + generalFileDialog.show(false, function(path) { + // Empty hash for latest + gui.dumpState("", path) + }) + } + } + + MenuSeparator {} + } + + Menu { + title: "Network" + MenuItem { + text: "Add Peer" + shortcut: "Ctrl+p" + onTriggered: { + addPeerWin.visible = true + } + } + MenuItem { + text: "Show Peers" + shortcut: "Ctrl+e" + onTriggered: { + peerWindow.visible = true + } + } + } + + Menu { + title: "Help" + MenuItem { + text: "About" + onTriggered: { + aboutWin.visible = true + } + } + } + + Menu { + title: "GLOBAL SHORTCUTS" + visible: false + MenuItem { + visible: false + shortcut: "Ctrl+l" + onTriggered: { + url.focus = true + } + } + } + } + + statusBar: StatusBar { + //height: 32 + visible: false + + id: statusBar + Label { + //y: 6 + id: walletValueLabel + + font.pixelSize: 10 + styleColor: "#797979" + } + + Label { + //y: 6 + objectName: "miningLabel" + visible: true + font.pixelSize: 10 + anchors.right: lastBlockLabel.left + anchors.rightMargin: 5 + } + + Label { + id: lastBlockLabel + objectName: "lastBlockLabel" + visible: true + text: "---" + font.pixelSize: 10 + anchors.right: peerGroup.left + anchors.rightMargin: 5 + } + + ProgressBar { + visible: false + id: downloadIndicator + value: 0 + objectName: "downloadIndicator" + y: -4 + x: statusBar.width / 2 - this.width / 2 + width: 160 + } + + Label { + visible: false + objectName: "downloadLabel" + //y: 7 + anchors.left: downloadIndicator.right + anchors.leftMargin: 5 + font.pixelSize: 10 + text: "0 / 0" + } + + + RowLayout { + id: peerGroup + //y: 7 + anchors.right: parent.right + MouseArea { + onDoubleClicked: peerWindow.visible = true + anchors.fill: parent + } + + Label { + id: peerLabel + font.pixelSize: 10 + text: "0 / 0" + } + } + } + + + property var blockModel: ListModel { + id: blockModel + } + + SplitView { + property var views: []; + + id: mainSplit + anchors.fill: parent + //resizing: false // this is NOT where we remove that damning resizing handle.. + handleDelegate: Item { + //This handle is a way to remove the line between the split views + Rectangle { + anchors.fill: parent + } + } + + function setView(view, menu) { + for(var i = 0; i < views.length; i++) { + views[i].view.visible = false + views[i].menuItem.setSelection(false) + } + view.visible = true + menu.setSelection(true) + } + + function addComponent(view, options) { + view.visible = false + view.anchors.fill = mainView + + var menuItem = menu.createMenuItem(view, options); + if( view.hasOwnProperty("menuItem") ) { + view.menuItem = menuItem; + } + + if( view.hasOwnProperty("onReady") ) { + view.onReady.call(view) + } + + if( options.active ) { + setView(view, menuItem) + } + + + return {view: view, menuItem: menuItem} + } + + /********************* + * Main menu. + ********************/ + Rectangle { + id: menu + Layout.minimumWidth: 192 + Layout.maximumWidth: 192 + + FontLoader { + id: sourceSansPro + source: "fonts/SourceSansPro-Regular.ttf" + } + FontLoader { + source: "fonts/SourceSansPro-Semibold.ttf" + } + FontLoader { + source: "fonts/SourceSansPro-Bold.ttf" + } + FontLoader { + source: "fonts/SourceSansPro-Black.ttf" + } + FontLoader { + source: "fonts/SourceSansPro-Light.ttf" + } + FontLoader { + source: "fonts/SourceSansPro-ExtraLight.ttf" + } + FontLoader { + id: simpleLineIcons + source: "fonts/Simple-Line-Icons.ttf" + } + + Rectangle { + color: "steelblue" + anchors.fill: parent + + MouseArea { + anchors.fill: parent + property real lastMouseX: 0 + property real lastMouseY: 0 + onPressed: { + lastMouseX = mouseX + lastMouseY = mouseY + } + onPositionChanged: { + root.x += (mouseX - lastMouseX) + root.y += (mouseY - lastMouseY) + } + /*onDoubleClicked: { + //!maximized ? view.set_max() : view.set_normal()} + visibility = "Minimized" + }*/ + } + } + + + + anchors.top: parent.top + Rectangle { + width: parent.height + height: parent.width + anchors.centerIn: parent + rotation: 90 + + gradient: Gradient { + GradientStop { position: 0.0; color: "#E2DEDE" } + GradientStop { position: 0.1; color: "#EBE8E8" } + GradientStop { position: 1.0; color: "#EBE8E8" } + } + } + + Component { + id: menuItemTemplate + Rectangle { + id: menuItem + property var view; + property var path; + property var closable; + + property alias title: label.text + property alias icon: icon.source + property alias secondaryTitle: secondary.text + function setSelection(on) { + sel.visible = on + + if (this.closable == true) { + closeIcon.visible = on + } + } + + function setAsBigButton(on) { + newAppButton.visible = on + label.visible = !on + buttonLabel.visible = on + } + + width: 192 + height: 55 + color: "#00000000" + + anchors { + left: parent.left + leftMargin: 4 + } + + Rectangle { + // New App Button + id: newAppButton + visible: false + anchors.fill: parent + anchors.rightMargin: 8 + border.width: 0 + radius: 5 + height: 55 + width: 180 + color: "#F3F1F3" + } + + Rectangle { + id: sel + visible: false + anchors.fill: parent + color: "#00000000" + Rectangle { + id: r + anchors.fill: parent + border.width: 0 + radius: 5 + color: "#FAFAFA" + } + Rectangle { + anchors { + top: r.top + bottom: r.bottom + right: r.right + } + width: 10 + color: "#FAFAFA" + border.width:0 + + Rectangle { + // Small line on top of selection. What's this for? + anchors { + left: parent.left + right: parent.right + top: parent.top + } + height: 1 + color: "#FAFAFA" + } + + Rectangle { + // Small line on bottom of selection. What's this for again? + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 1 + color: "#FAFAFA" + } + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: { + activeView(view, menuItem); + } + onEntered: { + if (parent.closable == true) { + closeIcon.visible = sel.visible + } + + } + onExited: { + closeIcon.visible = false + } + } + + Image { + id: icon + height: 24 + width: 24 + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 6 + } + } + + Text { + id: buttonLabel + visible: false + text: "GO TO NEW APP" + font.family: sourceSansPro.name + font.weight: Font.DemiBold + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: "#AAA0A0" + } + + Text { + id: label + font.family: sourceSansPro.name + font.weight: Font.DemiBold + anchors { + left: icon.right + verticalCenter: parent.verticalCenter + leftMargin: 6 + // verticalCenterOffset: -10 + } + x:250 + color: "#665F5F" + font.pixelSize: 14 + } + + + Text { + id: secondary + font.family: sourceSansPro.name + font.weight: Font.Light + anchors { + left: icon.right + leftMargin: 6 + top: label.bottom + } + color: "#6691C2" + font.pixelSize: 12 + } + + Rectangle { + id: closeIcon + visible: false + width: 10 + height: 10 + color: "#FFFFFF" + anchors { + fill: icon + } + + MouseArea { + anchors.fill: parent + onClicked: { + menuItem.closeApp() + } + } + + Text { + + font.family: simpleLineIcons.name + anchors { + centerIn: parent + } + color: "#665F5F" + font.pixelSize: 18 + text: "\ue082" + } + } + + + + function closeApp() { + if(!this.closable) { return; } + + if(this.view.hasOwnProperty("onDestroy")) { + this.view.onDestroy.call(this.view) + } + + this.view.destroy() + this.destroy() + for (var i = 0; i < mainSplit.views.length; i++) { + var view = mainSplit.views[i]; + if (view.menuItem === this) { + mainSplit.views.splice(i, 1); + break; + } + } + gui.removePlugin(this.path) + activeView(mainSplit.views[0].view, mainSplit.views[0].menuItem); + } + } + } + + function createMenuItem(view, options) { + if(options === undefined) { + options = {}; + } + + var section; + switch(options.section) { + case "begin": + section = menuBegin + break; + case "ethereum": + section = menuDefault; + break; + case "legacy": + section = menuLegacy; + break; + default: + section = menuApps; + break; + } + + var comp = menuItemTemplate.createObject(section) + comp.view = view + comp.title = view.title + + if(view.hasOwnProperty("iconSource")) { + comp.icon = view.iconSource; + } + comp.closable = options.close; + + if (options.section === "begin") { + comp.setAsBigButton(true) + } + + return comp + } + + ColumnLayout { + id: menuColumn + y: 10 + width: parent.width + anchors.left: parent.left + anchors.right: parent.right + spacing: 3 + + + + ColumnLayout { + id: menuBegin + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + Rectangle { + height: 55 + color: "transparent" + Text { + text: "ETHEREUM" + font.family: sourceSansPro.name + font.weight: Font.DemiBold + anchors { + left: parent.left + top: parent.verticalCenter + leftMargin: 16 + } + color: "#AAA0A0" + } + } + + + ColumnLayout { + id: menuDefault + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + Rectangle { + height: 55 + color: "transparent" + Text { + text: "APPS" + font.family: sourceSansPro.name + font.weight: Font.DemiBold + anchors { + left: parent.left + top: parent.verticalCenter + leftMargin: 16 + } + color: "#AAA0A0" + } + } + + ColumnLayout { + id: menuApps + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + + Rectangle { + height: 55 + color: "transparent" + Text { + text: "DEBUG" + font.family: sourceSansPro.name + font.weight: Font.DemiBold + anchors { + left: parent.left + top: parent.verticalCenter + leftMargin: 16 + } + color: "#AAA0A0" + } + } + + + ColumnLayout { + id: menuLegacy + spacing: 3 + anchors { + left: parent.left + right: parent.right + } + } + } + } + + /********************* + * Main view + ********************/ + Rectangle { + id: rootView + anchors.right: parent.right + anchors.left: menu.right + anchors.bottom: parent.bottom + anchors.top: parent.top + color: "#00000000" + + /*Rectangle { + id: urlPane + height: 40 + color: "#00000000" + anchors { + left: parent.left + right: parent.right + leftMargin: 5 + rightMargin: 5 + top: parent.top + topMargin: 5 + } + TextField { + id: url + objectName: "url" + placeholderText: "DApp URL" + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: 5 + rightMargin: 5 + leftMargin: 5 + } + + Keys.onReturnPressed: { + if(/^https?/.test(this.text)) { + newBrowserTab(this.text); + } else { + addPlugin(this.text, {close: true, section: "apps"}) + } + } + } + + } + + // Border + Rectangle { + id: divider + anchors { + left: parent.left + right: parent.right + top: urlPane.bottom + } + z: -1 + height: 1 + color: "#CCCCCC" + }*/ + + Rectangle { + id: mainView + color: "#00000000" + anchors.right: parent.right + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.top: parent.top + + function createView(component) { + var view = component.createObject(mainView) + + return view; + } + } + } + } + + + /****************** + * Dialogs + *****************/ + FileDialog { + id: generalFileDialog + property var callback; + onAccepted: { + var path = this.fileUrl.toString(); + callback.call(this, path); + } + + function show(selectExisting, callback) { + generalFileDialog.callback = callback; + generalFileDialog.selectExisting = selectExisting; + + this.open(); + } + } + + + /****************** + * Wallet functions + *****************/ + function importApp(path) { + var ext = path.split('.').pop() + if(ext == "html" || ext == "htm") { + eth.openHtml(path) + }else if(ext == "qml"){ + addPlugin(path, {close: true, section: "apps"}) + } + } + + + function setWalletValue(value) { + walletValueLabel.text = value + } + + function loadPlugin(name) { + console.log("Loading plugin" + name) + var view = mainView.addPlugin(name) + } + + function setPeers(text) { + peerLabel.text = text + } + + function addPeer(peer) { + // We could just append the whole peer object but it cries if you try to alter them + peerModel.append({ip: peer.ip, port: peer.port, lastResponse:timeAgo(peer.lastSend), latency: peer.latency, version: peer.version, caps: peer.caps}) + } + + function resetPeers(){ + peerModel.clear() + } + + function timeAgo(unixTs){ + var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 + return (lapsed + " seconds ago") + } + + function convertToPretty(unixTs){ + var a = new Date(unixTs*1000); + var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; + var year = a.getFullYear(); + var month = months[a.getMonth()]; + var date = a.getDate(); + var hour = a.getHours(); + var min = a.getMinutes(); + var sec = a.getSeconds(); + var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; + return time; + } + + /********************** + * Windows + *********************/ + Window { + id: peerWindow + //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint + height: 200 + width: 700 + Rectangle { + anchors.fill: parent + property var peerModel: ListModel { + id: peerModel + } + TableView { + anchors.fill: parent + id: peerTable + model: peerModel + TableViewColumn{width: 200; role: "ip" ; title: "IP" } + TableViewColumn{width: 260; role: "version" ; title: "Version" } + TableViewColumn{width: 180; role: "caps" ; title: "Capabilities" } + } + } + } + + Window { + id: aboutWin + visible: false + title: "About" + minimumWidth: 350 + maximumWidth: 350 + maximumHeight: 280 + minimumHeight: 280 + + Image { + id: aboutIcon + height: 150 + width: 150 + fillMode: Image.PreserveAspectFit + smooth: true + source: "../facet.png" + x: 10 + y: 30 + } + + Text { + anchors.left: aboutIcon.right + anchors.leftMargin: 10 + anchors.top: parent.top + anchors.topMargin: 30 + font.pointSize: 12 + text: "<h2>Mist (0.7.10)</h2><br><h3>Development</h3>Jeffrey Wilcke<br>Viktor TrĂ³n<br>Felix Lange<br>Taylor Gerring<br>Daniel Nagy<br><h3>UX</h3>Alex van de Sande<br>" + } + } + + Window { + id: txImportDialog + minimumWidth: 270 + maximumWidth: 270 + maximumHeight: 50 + minimumHeight: 50 + TextField { + id: txImportField + width: 170 + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 10 + onAccepted: { + } + } + Button { + anchors.left: txImportField.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 5 + text: "Import" + onClicked: { + eth.importTx(txImportField.text) + txImportField.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } + + Window { + id: addPeerWin + visible: false + minimumWidth: 300 + maximumWidth: 300 + maximumHeight: 50 + minimumHeight: 50 + title: "Connect to peer" + + ComboBox { + id: addrField + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: addPeerButton.left + anchors.leftMargin: 10 + anchors.rightMargin: 10 + onAccepted: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + + editable: true + model: ListModel { id: pastPeers } + + Component.onCompleted: { + pastPeers.insert(0, {text: "poc-8.ethdev.com:30303"}) + /* + var ips = eth.pastPeers() + for(var i = 0; i < ips.length; i++) { + pastPeers.append({text: ips.get(i)}) + } + + pastPeers.insert(0, {text: "poc-7.ethdev.com:30303"}) + */ + } + } + + Button { + id: addPeerButton + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.rightMargin: 10 + text: "Add" + onClicked: { + eth.connectToPeer(addrField.currentText) + addPeerWin.visible = false + } + } + Component.onCompleted: { + addrField.focus = true + } + } + } +======= id: root property var ethx : Eth.ethx @@ -874,3 +1968,4 @@ ApplicationWindow { } } } +>>>>>>> 32a9c0ca809508c1648b8f44f3e09725af7a80d3 |