github.com/jonasnick/go-ethereum@v0.7.12-0.20150216215225-22176f05d387/cmd/mist/assets/qml/main.qml (about) 1 import QtQuick 2.0 2 import QtQuick.Controls 1.0; 3 import QtQuick.Layouts 1.0; 4 import QtQuick.Dialogs 1.0; 5 import QtQuick.Window 2.1; 6 import QtQuick.Controls.Styles 1.1 7 import Ethereum 1.0 8 9 import "../ext/filter.js" as Eth 10 import "../ext/http.js" as Http 11 12 13 ApplicationWindow { 14 id: root 15 16 //flags: Qt.FramelessWindowHint 17 // Use this to make the window frameless. But then you'll need to do move and resize by hand 18 19 property var ethx : Eth.ethx 20 property var catalog; 21 22 width: 1200 23 height: 820 24 minimumHeight: 600 25 minimumWidth: 800 26 27 title: "Mist" 28 29 TextField { 30 id: copyElementHax 31 visible: false 32 } 33 34 function copyToClipboard(text) { 35 copyElementHax.text = text 36 copyElementHax.selectAll() 37 copyElementHax.copy() 38 } 39 40 // Takes care of loading all default plugins 41 Component.onCompleted: { 42 43 catalog = addPlugin("./views/catalog.qml", {noAdd: true, close: false, section: "begin"}); 44 var wallet = addPlugin("./views/wallet.qml", {noAdd: true, close: false, section: "ethereum", active: true}); 45 46 addPlugin("./views/miner.qml", {noAdd: true, close: false, section: "ethereum", active: true}); 47 addPlugin("./views/transaction.qml", {noAdd: true, close: false, section: "legacy"}); 48 addPlugin("./views/whisper.qml", {noAdd: true, close: false, section: "legacy"}); 49 addPlugin("./views/chain.qml", {noAdd: true, close: false, section: "legacy"}); 50 addPlugin("./views/pending_tx.qml", {noAdd: true, close: false, section: "legacy"}); 51 addPlugin("./views/info.qml", {noAdd: true, close: false, section: "legacy"}); 52 53 mainSplit.setView(catalog.view, catalog.menuItem); 54 55 //newBrowserTab("http://ethereum-dapp-catalog.meteor.com"); 56 57 // Command setup 58 gui.sendCommand(0) 59 } 60 61 function activeView(view, menuItem) { 62 mainSplit.setView(view, menuItem) 63 if (view.hideUrl) { 64 //urlPane.visible = false; 65 //mainView.anchors.top = rootView.top 66 } else { 67 //urlPane.visible = true; 68 //mainView.anchors.top = divider.bottom 69 } 70 } 71 72 function addViews(view, path, options) { 73 var views = mainSplit.addComponent(view, options) 74 views.menuItem.path = path 75 76 mainSplit.views.push(views); 77 78 if(!options.noAdd) { 79 gui.addPlugin(path) 80 } 81 82 return views 83 } 84 85 function addPlugin(path, options) { 86 try { 87 if(typeof(path) === "string" && /^https?/.test(path)) { 88 console.log('load http') 89 Http.request(path, function(o) { 90 if(o.status === 200) { 91 var view = Qt.createQmlObject(o.responseText, mainView, path) 92 addViews(view, path, options) 93 } 94 }) 95 96 return 97 } 98 99 var component = Qt.createComponent(path); 100 if(component.status != Component.Ready) { 101 if(component.status == Component.Error) { 102 ethx.note("error: ", component.errorString()); 103 } 104 105 return 106 } 107 108 var view = mainView.createView(component, options) 109 var views = addViews(view, path, options) 110 111 return views 112 } catch(e) { 113 console.log(e) 114 } 115 } 116 117 function newBrowserTab(url) { 118 119 var urlMatches = url.toString().match(/^[a-z]*\:\/\/([^\/?#]+)(?:[\/?#]|$)/i); 120 var requestedDomain = urlMatches && urlMatches[1]; 121 122 var domainAlreadyOpen = false; 123 124 console.log("requested: " + requestedDomain ) 125 126 for(var i = 0; i < mainSplit.views.length; i++) { 127 if (mainSplit.views[i].view.url) { 128 var matches = mainSplit.views[i].view.url.toString().match(/^[a-z]*\:\/\/(?:www\.)?([^\/?#]+)(?:[\/?#]|$)/i); 129 var existingDomain = matches && matches[1]; 130 console.log("exists: " + existingDomain); 131 if (requestedDomain == existingDomain) { 132 domainAlreadyOpen = true; 133 mainSplit.views[i].view.url = url; 134 activeView(mainSplit.views[i].view, mainSplit.views[i].menuItem); 135 } 136 } 137 } 138 139 if (!domainAlreadyOpen) { 140 var window = addPlugin("./views/browser.qml", {noAdd: true, close: true, section: "apps", active: true}); 141 window.view.url = url; 142 window.menuItem.title = "Mist"; 143 activeView(window.view, window.menuItem); 144 } 145 } 146 147 148 149 menuBar: MenuBar { 150 Menu { 151 title: "File" 152 MenuItem { 153 text: "New tab" 154 shortcut: "Ctrl+t" 155 onTriggered: { 156 activeView(catalog.view, catalog.menuItem); 157 } 158 } 159 160 MenuSeparator {} 161 162 MenuItem { 163 text: "Import key" 164 shortcut: "Ctrl+i" 165 onTriggered: { 166 generalFileDialog.show(true, function(path) { 167 gui.importKey(path) 168 }) 169 } 170 } 171 172 MenuItem { 173 text: "Export keys" 174 shortcut: "Ctrl+e" 175 onTriggered: { 176 generalFileDialog.show(false, function(path) { 177 }) 178 } 179 } 180 181 } 182 183 Menu { 184 title: "Developer" 185 MenuItem { 186 iconSource: "../icecream.png" 187 text: "Debugger" 188 shortcut: "Ctrl+d" 189 onTriggered: eth.startDebugger() 190 } 191 192 MenuItem { 193 text: "Import Tx" 194 onTriggered: { 195 txImportDialog.visible = true 196 } 197 } 198 199 MenuItem { 200 text: "Run JS file" 201 onTriggered: { 202 generalFileDialog.show(true, function(path) { 203 eth.evalJavascriptFile(path) 204 }) 205 } 206 } 207 208 MenuItem { 209 text: "Dump state" 210 onTriggered: { 211 generalFileDialog.show(false, function(path) { 212 // Empty hash for latest 213 gui.dumpState("", path) 214 }) 215 } 216 } 217 218 MenuSeparator {} 219 } 220 221 Menu { 222 title: "Network" 223 MenuItem { 224 text: "Add Peer" 225 shortcut: "Ctrl+p" 226 onTriggered: { 227 addPeerWin.visible = true 228 } 229 } 230 MenuItem { 231 text: "Show Peers" 232 shortcut: "Ctrl+e" 233 onTriggered: { 234 peerWindow.visible = true 235 } 236 } 237 } 238 239 Menu { 240 title: "Help" 241 MenuItem { 242 text: "About" 243 onTriggered: { 244 aboutWin.visible = true 245 } 246 } 247 } 248 } 249 250 statusBar: StatusBar { 251 //height: 32 252 visible: false 253 254 id: statusBar 255 Label { 256 //y: 6 257 id: walletValueLabel 258 259 font.pixelSize: 10 260 styleColor: "#797979" 261 } 262 263 /* 264 Label { 265 //y: 6 266 objectName: "miningLabel" 267 visible: true 268 font.pixelSize: 10 269 anchors.right: lastBlockLabel.left 270 anchors.rightMargin: 5 271 } 272 273 Label { 274 id: lastBlockLabel 275 objectName: "lastBlockLabel" 276 visible: true 277 text: "---" 278 font.pixelSize: 10 279 anchors.right: peerGroup.left 280 anchors.rightMargin: 5 281 } 282 */ 283 284 ProgressBar { 285 visible: false 286 id: downloadIndicator 287 value: 0 288 objectName: "downloadIndicator" 289 y: -4 290 x: statusBar.width / 2 - this.width / 2 291 width: 160 292 } 293 294 Label { 295 visible: false 296 objectName: "downloadLabel" 297 //y: 7 298 anchors.left: downloadIndicator.right 299 anchors.leftMargin: 5 300 font.pixelSize: 10 301 text: "0 / 0" 302 } 303 304 305 RowLayout { 306 id: peerGroup 307 //y: 7 308 anchors.right: parent.right 309 MouseArea { 310 onDoubleClicked: peerWindow.visible = true 311 anchors.fill: parent 312 } 313 314 Label { 315 id: peerCounterLabel 316 font.pixelSize: 10 317 text: "0 / 0" 318 } 319 } 320 } 321 322 323 property var blockModel: ListModel { 324 id: blockModel 325 } 326 327 SplitView { 328 property var views: []; 329 330 id: mainSplit 331 anchors.fill: parent 332 //resizing: false // this is NOT where we remove that damning resizing handle.. 333 handleDelegate: Item { 334 //This handle is a way to remove the line between the split views 335 Rectangle { 336 anchors.fill: parent 337 } 338 } 339 340 function setView(view, menu) { 341 for(var i = 0; i < views.length; i++) { 342 views[i].view.visible = false 343 views[i].menuItem.setSelection(false) 344 } 345 view.visible = true 346 menu.setSelection(true) 347 } 348 349 function addComponent(view, options) { 350 view.visible = false 351 view.anchors.fill = mainView 352 353 var menuItem = menu.createMenuItem(view, options); 354 if( view.hasOwnProperty("menuItem") ) { 355 view.menuItem = menuItem; 356 } 357 358 if( view.hasOwnProperty("onReady") ) { 359 view.onReady.call(view) 360 } 361 362 if( options.active ) { 363 setView(view, menuItem) 364 } 365 366 367 return {view: view, menuItem: menuItem} 368 } 369 370 /********************* 371 * Main menu. 372 ********************/ 373 Rectangle { 374 id: menu 375 Layout.minimumWidth: 192 376 Layout.maximumWidth: 192 377 378 FontLoader { 379 id: sourceSansPro 380 source: "fonts/SourceSansPro-Regular.ttf" 381 } 382 FontLoader { 383 source: "fonts/SourceSansPro-Semibold.ttf" 384 } 385 FontLoader { 386 source: "fonts/SourceSansPro-Bold.ttf" 387 } 388 FontLoader { 389 source: "fonts/SourceSansPro-Black.ttf" 390 } 391 FontLoader { 392 source: "fonts/SourceSansPro-Light.ttf" 393 } 394 FontLoader { 395 source: "fonts/SourceSansPro-ExtraLight.ttf" 396 } 397 FontLoader { 398 id: simpleLineIcons 399 source: "fonts/Simple-Line-Icons.ttf" 400 } 401 402 Rectangle { 403 color: "steelblue" 404 anchors.fill: parent 405 406 MouseArea { 407 anchors.fill: parent 408 property real lastMouseX: 0 409 property real lastMouseY: 0 410 onPressed: { 411 lastMouseX = mouseX 412 lastMouseY = mouseY 413 } 414 onPositionChanged: { 415 root.x += (mouseX - lastMouseX) 416 root.y += (mouseY - lastMouseY) 417 } 418 /*onDoubleClicked: { 419 //!maximized ? view.set_max() : view.set_normal()} 420 visibility = "Minimized" 421 }*/ 422 } 423 } 424 425 426 427 anchors.top: parent.top 428 Rectangle { 429 width: parent.height 430 height: parent.width 431 anchors.centerIn: parent 432 rotation: 90 433 434 gradient: Gradient { 435 GradientStop { position: 0.0; color: "#E2DEDE" } 436 GradientStop { position: 0.1; color: "#EBE8E8" } 437 GradientStop { position: 1.0; color: "#EBE8E8" } 438 } 439 } 440 441 Component { 442 id: menuItemTemplate 443 Rectangle { 444 id: menuItem 445 property var view; 446 property var path; 447 property var closable; 448 449 property alias title: label.text 450 property alias icon: icon.source 451 property alias secondaryTitle: secondary.text 452 function setSelection(on) { 453 sel.visible = on 454 455 if (this.closable == true) { 456 closeIcon.visible = on 457 } 458 } 459 460 function setAsBigButton(on) { 461 newAppButton.visible = on 462 label.visible = !on 463 buttonLabel.visible = on 464 } 465 466 width: 192 467 height: 55 468 color: "#00000000" 469 470 anchors { 471 left: parent.left 472 leftMargin: 4 473 } 474 475 Rectangle { 476 // New App Button 477 id: newAppButton 478 visible: false 479 anchors.fill: parent 480 anchors.rightMargin: 8 481 border.width: 0 482 radius: 5 483 height: 55 484 width: 180 485 color: "#F3F1F3" 486 } 487 488 Rectangle { 489 id: sel 490 visible: false 491 anchors.fill: parent 492 color: "#00000000" 493 Rectangle { 494 id: r 495 anchors.fill: parent 496 border.width: 0 497 radius: 5 498 color: "#FAFAFA" 499 } 500 Rectangle { 501 anchors { 502 top: r.top 503 bottom: r.bottom 504 right: r.right 505 } 506 width: 10 507 color: "#FAFAFA" 508 border.width:0 509 510 Rectangle { 511 // Small line on top of selection. What's this for? 512 anchors { 513 left: parent.left 514 right: parent.right 515 top: parent.top 516 } 517 height: 1 518 color: "#FAFAFA" 519 } 520 521 Rectangle { 522 // Small line on bottom of selection. What's this for again? 523 anchors { 524 left: parent.left 525 right: parent.right 526 bottom: parent.bottom 527 } 528 height: 1 529 color: "#FAFAFA" 530 } 531 } 532 } 533 534 MouseArea { 535 anchors.fill: parent 536 hoverEnabled: true 537 onClicked: { 538 activeView(view, menuItem); 539 } 540 onEntered: { 541 if (parent.closable == true) { 542 closeIcon.visible = sel.visible 543 } 544 545 } 546 onExited: { 547 closeIcon.visible = false 548 } 549 } 550 551 Image { 552 id: icon 553 height: 24 554 width: 24 555 anchors { 556 left: parent.left 557 verticalCenter: parent.verticalCenter 558 leftMargin: 6 559 } 560 } 561 562 Text { 563 id: buttonLabel 564 visible: false 565 text: "GO TO NEW APP" 566 font.family: sourceSansPro.name 567 font.weight: Font.DemiBold 568 anchors.horizontalCenter: parent.horizontalCenter 569 anchors.verticalCenter: parent.verticalCenter 570 color: "#AAA0A0" 571 } 572 573 Text { 574 id: label 575 font.family: sourceSansPro.name 576 font.weight: Font.DemiBold 577 anchors { 578 left: icon.right 579 verticalCenter: parent.verticalCenter 580 leftMargin: 6 581 // verticalCenterOffset: -10 582 } 583 x:250 584 color: "#665F5F" 585 font.pixelSize: 14 586 } 587 588 589 Text { 590 id: secondary 591 font.family: sourceSansPro.name 592 font.weight: Font.Light 593 anchors { 594 left: icon.right 595 leftMargin: 6 596 top: label.bottom 597 } 598 color: "#6691C2" 599 font.pixelSize: 12 600 } 601 602 Rectangle { 603 id: closeIcon 604 visible: false 605 width: 10 606 height: 10 607 color: "#FFFFFF" 608 anchors { 609 fill: icon 610 } 611 612 MouseArea { 613 anchors.fill: parent 614 onClicked: { 615 menuItem.closeApp() 616 } 617 } 618 619 Text { 620 621 font.family: simpleLineIcons.name 622 anchors { 623 centerIn: parent 624 } 625 color: "#665F5F" 626 font.pixelSize: 18 627 text: "\ue082" 628 } 629 } 630 631 632 633 function closeApp() { 634 if(!this.closable) { return; } 635 636 if(this.view.hasOwnProperty("onDestroy")) { 637 this.view.onDestroy.call(this.view) 638 } 639 640 this.view.destroy() 641 this.destroy() 642 for (var i = 0; i < mainSplit.views.length; i++) { 643 var view = mainSplit.views[i]; 644 if (view.menuItem === this) { 645 mainSplit.views.splice(i, 1); 646 break; 647 } 648 } 649 gui.removePlugin(this.path) 650 activeView(mainSplit.views[0].view, mainSplit.views[0].menuItem); 651 } 652 } 653 } 654 655 function createMenuItem(view, options) { 656 if(options === undefined) { 657 options = {}; 658 } 659 660 var section; 661 switch(options.section) { 662 case "begin": 663 section = menuBegin 664 break; 665 case "ethereum": 666 section = menuDefault; 667 break; 668 case "legacy": 669 section = menuLegacy; 670 break; 671 default: 672 section = menuApps; 673 break; 674 } 675 676 var comp = menuItemTemplate.createObject(section) 677 comp.view = view 678 comp.title = view.title 679 680 if(view.hasOwnProperty("iconSource")) { 681 comp.icon = view.iconSource; 682 } 683 comp.closable = options.close; 684 685 if (options.section === "begin") { 686 comp.setAsBigButton(true) 687 } 688 689 return comp 690 } 691 692 ColumnLayout { 693 id: menuColumn 694 y: 10 695 width: parent.width 696 anchors.left: parent.left 697 anchors.right: parent.right 698 spacing: 3 699 700 701 702 ColumnLayout { 703 id: menuBegin 704 spacing: 3 705 anchors { 706 left: parent.left 707 right: parent.right 708 } 709 } 710 711 Rectangle { 712 height: 55 713 color: "transparent" 714 Text { 715 text: "ETHEREUM" 716 font.family: sourceSansPro.name 717 font.weight: Font.DemiBold 718 anchors { 719 left: parent.left 720 top: parent.verticalCenter 721 leftMargin: 16 722 } 723 color: "#AAA0A0" 724 } 725 } 726 727 728 ColumnLayout { 729 id: menuDefault 730 spacing: 3 731 anchors { 732 left: parent.left 733 right: parent.right 734 } 735 } 736 737 Rectangle { 738 height: 55 739 color: "transparent" 740 Text { 741 text: "APPS" 742 font.family: sourceSansPro.name 743 font.weight: Font.DemiBold 744 anchors { 745 left: parent.left 746 top: parent.verticalCenter 747 leftMargin: 16 748 } 749 color: "#AAA0A0" 750 } 751 } 752 753 ColumnLayout { 754 id: menuApps 755 spacing: 3 756 anchors { 757 left: parent.left 758 right: parent.right 759 } 760 } 761 762 Rectangle { 763 height: 55 764 color: "transparent" 765 Text { 766 text: "DEBUG" 767 font.family: sourceSansPro.name 768 font.weight: Font.DemiBold 769 anchors { 770 left: parent.left 771 top: parent.verticalCenter 772 leftMargin: 16 773 } 774 color: "#AAA0A0" 775 } 776 } 777 778 779 ColumnLayout { 780 id: menuLegacy 781 spacing: 3 782 anchors { 783 left: parent.left 784 right: parent.right 785 } 786 } 787 } 788 } 789 790 /********************* 791 * Main view 792 ********************/ 793 Rectangle { 794 id: rootView 795 anchors.right: parent.right 796 anchors.left: menu.right 797 anchors.bottom: parent.bottom 798 anchors.top: parent.top 799 color: "#00000000" 800 801 /*Rectangle { 802 id: urlPane 803 height: 40 804 color: "#00000000" 805 anchors { 806 left: parent.left 807 right: parent.right 808 leftMargin: 5 809 rightMargin: 5 810 top: parent.top 811 topMargin: 5 812 } 813 TextField { 814 id: url 815 objectName: "url" 816 placeholderText: "DApp URL" 817 anchors { 818 left: parent.left 819 right: parent.right 820 top: parent.top 821 topMargin: 5 822 rightMargin: 5 823 leftMargin: 5 824 } 825 826 Keys.onReturnPressed: { 827 if(/^https?/.test(this.text)) { 828 newBrowserTab(this.text); 829 } else { 830 addPlugin(this.text, {close: true, section: "apps"}) 831 } 832 } 833 } 834 835 } 836 837 // Border 838 Rectangle { 839 id: divider 840 anchors { 841 left: parent.left 842 right: parent.right 843 top: urlPane.bottom 844 } 845 z: -1 846 height: 1 847 color: "#CCCCCC" 848 }*/ 849 850 Rectangle { 851 id: mainView 852 color: "#00000000" 853 anchors.right: parent.right 854 anchors.left: parent.left 855 anchors.bottom: parent.bottom 856 anchors.top: parent.top 857 858 function createView(component) { 859 var view = component.createObject(mainView) 860 861 return view; 862 } 863 } 864 } 865 } 866 867 868 /****************** 869 * Dialogs 870 *****************/ 871 FileDialog { 872 id: generalFileDialog 873 property var callback; 874 onAccepted: { 875 var path = this.fileUrl.toString(); 876 callback.call(this, path); 877 } 878 879 function show(selectExisting, callback) { 880 generalFileDialog.callback = callback; 881 generalFileDialog.selectExisting = selectExisting; 882 883 this.open(); 884 } 885 } 886 887 888 /****************** 889 * Wallet functions 890 *****************/ 891 function importApp(path) { 892 var ext = path.split('.').pop() 893 if(ext == "html" || ext == "htm") { 894 eth.openHtml(path) 895 }else if(ext == "qml"){ 896 addPlugin(path, {close: true, section: "apps"}) 897 } 898 } 899 900 function setWalletValue(value) { 901 walletValueLabel.text = value 902 } 903 904 function loadPlugin(name) { 905 console.log("Loading plugin" + name) 906 var view = mainView.addPlugin(name) 907 } 908 909 function clearPeers() { peerModel.clear() } 910 function addPeer(peer) { peerModel.append(peer) } 911 912 function setPeerCounters(text) { 913 peerCounterLabel.text = text 914 } 915 916 function timeAgo(unixTs){ 917 var lapsed = (Date.now() - new Date(unixTs*1000)) / 1000 918 return (lapsed + " seconds ago") 919 } 920 921 function convertToPretty(unixTs){ 922 var a = new Date(unixTs*1000); 923 var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; 924 var year = a.getFullYear(); 925 var month = months[a.getMonth()]; 926 var date = a.getDate(); 927 var hour = a.getHours(); 928 var min = a.getMinutes(); 929 var sec = a.getSeconds(); 930 var time = date+' '+month+' '+year+' '+hour+':'+min+':'+sec ; 931 return time; 932 } 933 934 /********************** 935 * Windows 936 *********************/ 937 Window { 938 id: peerWindow 939 //flags: Qt.CustomizeWindowHint | Qt.Tool | Qt.WindowCloseButtonHint 940 height: 200 941 width: 700 942 Rectangle { 943 anchors.fill: parent 944 property var peerModel: ListModel { 945 id: peerModel 946 } 947 TableView { 948 anchors.fill: parent 949 id: peerTable 950 model: peerModel 951 TableViewColumn{width: 180; role: "addr" ; title: "Remote Address" } 952 TableViewColumn{width: 280; role: "nodeID" ; title: "Node ID" } 953 TableViewColumn{width: 180; role: "caps" ; title: "Capabilities" } 954 } 955 } 956 } 957 958 Window { 959 id: aboutWin 960 visible: false 961 title: "About" 962 minimumWidth: 350 963 maximumWidth: 350 964 maximumHeight: 280 965 minimumHeight: 280 966 967 Image { 968 id: aboutIcon 969 height: 150 970 width: 150 971 fillMode: Image.PreserveAspectFit 972 smooth: true 973 source: "../facet.png" 974 x: 10 975 y: 30 976 } 977 978 Text { 979 anchors.left: aboutIcon.right 980 anchors.leftMargin: 10 981 anchors.top: parent.top 982 anchors.topMargin: 30 983 font.pointSize: 12 984 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>" 985 } 986 } 987 988 Window { 989 id: txImportDialog 990 minimumWidth: 270 991 maximumWidth: 270 992 maximumHeight: 50 993 minimumHeight: 50 994 TextField { 995 id: txImportField 996 width: 170 997 anchors.verticalCenter: parent.verticalCenter 998 anchors.left: parent.left 999 anchors.leftMargin: 10 1000 onAccepted: { 1001 } 1002 } 1003 Button { 1004 anchors.left: txImportField.right 1005 anchors.verticalCenter: parent.verticalCenter 1006 anchors.leftMargin: 5 1007 text: "Import" 1008 onClicked: { 1009 eth.importTx(txImportField.text) 1010 txImportField.visible = false 1011 } 1012 } 1013 Component.onCompleted: { 1014 addrField.focus = true 1015 } 1016 } 1017 1018 Window { 1019 id: addPeerWin 1020 visible: false 1021 minimumWidth: 400 1022 maximumWidth: 400 1023 maximumHeight: 50 1024 minimumHeight: 50 1025 title: "Connect to peer" 1026 1027 TextField { 1028 id: addrField 1029 anchors.verticalCenter: parent.verticalCenter 1030 anchors.left: parent.left 1031 anchors.right: addPeerButton.left 1032 anchors.leftMargin: 10 1033 anchors.rightMargin: 10 1034 placeholderText: "enode://<hex node id>:<IP address>:<port>" 1035 onAccepted: { 1036 if(addrField.text.length != 0) { 1037 eth.connectToPeer(addrField.text) 1038 addPeerWin.visible = false 1039 } 1040 } 1041 } 1042 1043 Button { 1044 id: addPeerButton 1045 anchors.right: parent.right 1046 anchors.verticalCenter: parent.verticalCenter 1047 anchors.rightMargin: 10 1048 text: "Connect" 1049 onClicked: { 1050 if(addrField.text.length != 0) { 1051 eth.connectToPeer(addrField.text) 1052 addPeerWin.visible = false 1053 } 1054 } 1055 } 1056 Component.onCompleted: { 1057 addrField.focus = true 1058 } 1059 } 1060 }