Commit 0c098f15 authored by Dan Pascu's avatar Dan Pascu

Reworked the server tools window


- added navigation capabilities to the Web viewer
- implemented missing functionality (opening external links and SIP URLs)
- enhanced the window look to better fit Blink's overall theme
parent be9d7114
......@@ -8,10 +8,11 @@ import urllib2
from PyQt5 import uic
from PyQt5.QtCore import Qt, QAbstractListModel, QModelIndex, QSortFilterProxyModel, QUrl, QUrlQuery
from PyQt5.QtGui import QIcon, QMovie
from PyQt5.QtGui import QIcon
from PyQt5.QtNetwork import QNetworkAccessManager
from PyQt5.QtWebKitWidgets import QWebView
from PyQt5.QtWidgets import QButtonGroup, QComboBox, QMenu
from PyQt5.QtWebKit import QWebSettings
from PyQt5.QtWebKitWidgets import QWebView, QWebPage
from PyQt5.QtWidgets import QApplication, QButtonGroup, QComboBox, QMenu
from application.notification import IObserver, NotificationCenter
from application.python import Null
......@@ -27,7 +28,10 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.threading import run_in_thread
from sipsimple.util import user_info
from blink.resources import ApplicationData, Resources
from blink.configuration.settings import BlinkSettings
from blink.contacts import URIUtils
from blink.resources import ApplicationData, IconManager, Resources
from blink.sessions import SessionManager, StreamDescription
from blink.widgets.labels import Status
from blink.util import QSingleton, call_in_gui_thread, run_in_gui_thread
......@@ -522,6 +526,29 @@ del ui_class, base_class
# Account server tools
#
class WebPage(QWebPage):
def __init__(self, parent=None):
super(WebPage, self).__init__(parent)
disable_actions = {QWebPage.OpenLink, QWebPage.OpenLinkInNewWindow, QWebPage.OpenLinkInThisWindow, QWebPage.OpenFrameInNewWindow, QWebPage.DownloadLinkToDisk,
QWebPage.OpenImageInNewWindow, QWebPage.DownloadImageToDisk, QWebPage.DownloadMediaToDisk}
for action in (self.action(action) for action in disable_actions):
action.setVisible(False)
def createWindow(self, type):
return self
def acceptNavigationRequest(self, frame, request, navigation_type):
if navigation_type == QWebPage.NavigationTypeLinkClicked and self.linkDelegationPolicy() == QWebPage.DontDelegateLinks and request.url().scheme() in ('sip', 'sips'):
blink = QApplication.instance()
contact, contact_uri = URIUtils.find_contact(request.url().toString())
session_manager = SessionManager()
session_manager.create_session(contact, contact_uri, [StreamDescription('audio')])
blink.main_window.raise_()
blink.main_window.activateWindow()
return False
return super(WebPage, self).acceptNavigationRequest(frame, request, navigation_type)
class ServerToolsAccountModel(QSortFilterProxyModel):
def __init__(self, model, parent=None):
super(ServerToolsAccountModel, self).__init__(parent)
......@@ -540,6 +567,7 @@ class ServerToolsWebView(QWebView):
def __init__(self, parent=None):
super(ServerToolsWebView, self).__init__(parent)
self.setPage(WebPage(self))
self.access_manager = Null
self.authenticated = False
self.account = None
......@@ -548,7 +576,10 @@ class ServerToolsWebView(QWebView):
self.task = None
self.last_error = None
self.realm = None
self.homepage = None
self.urlChanged.connect(self._SH_URLChanged)
self.settings().setAttribute(QWebSettings.JavascriptEnabled, True)
self.settings().setAttribute(QWebSettings.JavascriptCanOpenWindows, True)
@property
def query_items(self):
......@@ -614,7 +645,7 @@ class ServerToolsWebView(QWebView):
self.tab = query_items.get('tab') or self.tab
self.task = query_items.get('task') or self.task
def load_account_page(self, account, tab=None, task=None):
def load_account_page(self, account, tab=None, task=None, reset_history=False, set_home=False):
self.tab = tab
self.task = task
self.account = account
......@@ -623,7 +654,16 @@ class ServerToolsWebView(QWebView):
for name, value in self.query_items:
url_query.addQueryItem(name, value)
url.setQuery(url_query)
self.load(url)
if set_home:
self.homepage = url
if reset_history:
self.history().clear()
self.page().mainFrame().evaluateJavaScript('window.location.replace("{}");'.format(url.toString())) # this will replace the current url in the history
else:
self.load(url)
def load_homepage(self):
self.load(self.homepage or self.history().itemAt(0).url())
ui_class, base_class = uic.loadUiType(Resources.get('server_tools.ui'))
......@@ -632,56 +672,69 @@ ui_class, base_class = uic.loadUiType(Resources.get('server_tools.ui'))
class ServerToolsWindow(base_class, ui_class):
__metaclass__ = QSingleton
implements(IObserver)
def __init__(self, model, parent=None):
super(ServerToolsWindow, self).__init__(parent)
with Resources.directory:
self.setupUi(self)
self.spinner_movie = QMovie(Resources.get('icons/servertools-spinner.mng'))
self.spinner_label.setMovie(self.spinner_movie)
self.spinner_label.hide()
self.progress_bar.hide()
while self.tab_widget.count():
self.tab_widget.removeTab(0) # remove the tab(s) added in designer
self.tab_widget.tabBar().hide()
self.account_button.setMenu(QMenu(self.account_button))
self.setupUi()
self.setWindowTitle('Blink Server Tools')
self.setWindowIconText('Server Tools')
self.setWindowIcon(QIcon(Resources.get('icons/blink48.png')))
self.model = model
self.tab_widget.addTab(ServerToolsWebView(self), '')
font = self.account_label.font()
font.setPointSizeF(self.account_label.fontInfo().pointSizeF() + 2)
font.setFamily("Sans Serif")
self.account_label.setFont(font)
self.model.rowsInserted.connect(self._SH_ModelChanged)
self.model.rowsRemoved.connect(self._SH_ModelChanged)
self.account_button.menu().triggered.connect(self._SH_AccountButtonMenuTriggered)
web_view = self.tab_widget.currentWidget()
web_view.loadStarted.connect(self._SH_WebViewLoadStarted)
web_view.loadFinished.connect(self._SH_WebViewLoadFinished)
web_view.loadProgress.connect(self._SH_WebViewLoadProgress)
self.back_button.clicked.connect(self._SH_BackButtonClicked)
self.back_button.triggered.connect(self._SH_NavigationButtonTriggered)
self.forward_button.clicked.connect(self._SH_ForwardButtonClicked)
self.forward_button.triggered.connect(self._SH_NavigationButtonTriggered)
self.home_button.clicked.connect(self._SH_HomeButtonClicked)
self.web_view.loadStarted.connect(self._SH_WebViewLoadStarted)
self.web_view.loadFinished.connect(self._SH_WebViewLoadFinished)
self.web_view.titleChanged.connect(self._SH_WebViewTitleChanged)
notification_center = NotificationCenter()
notification_center.add_observer(self, name='SIPApplicationDidStart')
def setupUi(self):
super(ServerToolsWindow, self).setupUi(self)
self.account_button.default_avatar = QIcon(Resources.get('icons/default-avatar.png'))
self.account_button.setIcon(IconManager().get('avatar') or self.account_button.default_avatar)
self.account_button.setMenu(QMenu(self.account_button))
self.back_button.setMenu(QMenu(self.back_button))
self.back_button.setEnabled(False)
self.forward_button.setMenu(QMenu(self.forward_button))
self.forward_button.setEnabled(False)
def _SH_AccountButtonMenuTriggered(self, action):
view = self.tab_widget.currentWidget()
account = action.data()
self.account_label.setText(account.id)
self.tab_widget.setTabText(self.tab_widget.currentIndex(), account.id)
view.load_account_page(account, tab=view.tab, task=view.task)
account_changed = account is not self.web_view.account
if account_changed:
self.back_button.setEnabled(False)
self.forward_button.setEnabled(False)
self.account_button.setText(account.id)
self.web_view.load_account_page(account, tab=self.web_view.tab, task=self.web_view.task, reset_history=account_changed, set_home=account_changed)
def _SH_BackButtonClicked(self):
self.web_view.history().back()
def _SH_ForwardButtonClicked(self):
self.web_view.history().forward()
def _SH_NavigationButtonTriggered(self, action):
self.web_view.history().goToItem(action.history_item)
def _SH_HomeButtonClicked(self):
self.web_view.load_homepage()
def _SH_WebViewLoadStarted(self):
self.spinner_label.show()
self.spinner_movie.start()
self.progress_bar.setValue(0)
# self.progress_bar.show()
self.spinner.show()
def _SH_WebViewLoadFinished(self, load_ok):
self.spinner_movie.stop()
self.spinner_label.hide()
self.progress_bar.hide()
self.spinner.hide()
if not load_ok:
web_view = self.tab_widget.currentWidget()
icon_path = Resources.get('icons/invalid.png')
error_message = web_view.last_error or 'Unknown error'
error_message = self.web_view.last_error or 'Unknown error'
html = """
<html>
<head>
......@@ -696,14 +749,13 @@ class ServerToolsWindow(base_class, ui_class):
</body>
</html>
""" % (icon_path, error_message)
web_view.loadStarted.disconnect(self._SH_WebViewLoadStarted)
web_view.loadFinished.disconnect(self._SH_WebViewLoadFinished)
web_view.setHtml(html)
web_view.loadStarted.connect(self._SH_WebViewLoadStarted)
web_view.loadFinished.connect(self._SH_WebViewLoadFinished)
self.web_view.blockSignals(True)
self.web_view.setHtml(html)
self.web_view.blockSignals(False)
self._update_navigation_buttons()
def _SH_WebViewLoadProgress(self, percent):
self.progress_bar.setValue(percent)
def _SH_WebViewTitleChanged(self, title):
self.window().setWindowTitle(u'Blink Server Tools: {}'.format(title))
def _SH_ModelChanged(self, parent_index, start, end):
menu = self.account_button.menu()
......@@ -714,35 +766,67 @@ class ServerToolsWindow(base_class, ui_class):
action.setData(account_info.account)
def open_settings_page(self, account):
view = self.tab_widget.currentWidget()
account = account or view.account
account = account or self.web_view.account
if account is None or account.server.settings_url is None:
account = self.account_button.menu().actions()[0].data()
self.account_label.setText(account.id)
self.tab_widget.setTabText(self.tab_widget.currentIndex(), account.id)
view.load_account_page(account, tab='settings')
account_changed = account is not self.web_view.account
if account_changed:
self.back_button.setEnabled(False)
self.forward_button.setEnabled(False)
self.account_button.setText(account.id)
self.web_view.load_account_page(account, tab='settings', reset_history=account_changed, set_home=True)
self.show()
def open_search_for_people_page(self, account):
view = self.tab_widget.currentWidget()
account = account or view.account
account = account or self.web_view.account
if account is None or account.server.settings_url is None:
account = self.account_button.menu().actions()[0].data()
self.account_label.setText(account.id)
self.tab_widget.setTabText(self.tab_widget.currentIndex(), account.id)
view.load_account_page(account, tab='contacts', task='directory')
account_changed = account is not self.web_view.account
if account_changed:
self.back_button.setEnabled(False)
self.forward_button.setEnabled(False)
self.account_button.setText(account.id)
self.web_view.load_account_page(account, tab='contacts', task='directory', reset_history=account_changed, set_home=True)
self.show()
def open_history_page(self, account):
view = self.tab_widget.currentWidget()
account = account or view.account
account = account or self.web_view.account
if account is None or account.server.settings_url is None:
account = self.account_button.menu().actions()[0].data()
self.account_label.setText(account.id)
self.tab_widget.setTabText(self.tab_widget.currentIndex(), account.id)
view.load_account_page(account, tab='calls')
account_changed = account is not self.web_view.account
if account_changed:
self.back_button.setEnabled(False)
self.forward_button.setEnabled(False)
self.account_button.setText(account.id)
self.web_view.load_account_page(account, tab='calls', reset_history=account_changed, set_home=True)
self.show()
del ui_class, base_class
def _update_navigation_buttons(self):
history = self.web_view.history()
self.back_button.setEnabled(history.canGoBack())
self.forward_button.setEnabled(history.canGoForward())
back_menu = self.back_button.menu()
back_menu.clear()
for item in reversed(history.backItems(7)):
action = back_menu.addAction(item.title())
action.history_item = item
forward_menu = self.forward_button.menu()
forward_menu.clear()
for item in history.forwardItems(7):
action = forward_menu.addAction(item.title())
action.history_item = item
@run_in_gui_thread
def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, Null)
handler(notification)
def _NH_SIPApplicationDidStart(self, notification):
notification.center.add_observer(self, name='CFGSettingsObjectDidChange', sender=BlinkSettings())
def _NH_CFGSettingsObjectDidChange(self, notification):
if 'presence.icon' in notification.data.modified:
self.account_button.setIcon(IconManager().get('avatar') or self.account_button.default_avatar)
del ui_class, base_class
from __future__ import division
from PyQt5.QtCore import QEvent, QRectF, QSize
from PyQt5.QtSvg import QSvgWidget
from blink.resources import Resources
from blink.widgets.util import QtDynamicProperty
__all__ = ['Spinner']
class Spinner(QSvgWidget):
icon_file = QtDynamicProperty('icon_file', type=unicode)
icon_size = QtDynamicProperty('icon_size', type=QSize)
icon_crop = QtDynamicProperty('icon_crop', type=int)
def __init__(self, parent=None, icon='icons/spinner.svg'):
super(Spinner, self).__init__(parent)
self._original_viewbox = QRectF()
self.icon_crop = 0
self.icon_size = None
self.icon_file = Resources.get(icon)
def load(self, svg):
super(Spinner, self).load(svg)
self._original_viewbox = self.renderer().viewBoxF()
self._update_viewbox(self.size())
def event(self, event):
if event.type() == QEvent.DynamicPropertyChange:
if event.propertyName() == 'icon_crop':
self._update_viewbox(self.size())
elif event.propertyName() == 'icon_file':
self.load(self.icon_file)
elif event.propertyName() == 'icon_size':
self.updateGeometry()
return super(Spinner, self).event(event)
def resizeEvent(self, event):
super(Spinner, self).resizeEvent(event)
self._update_viewbox(event.size())
def sizeHint(self):
return self.icon_size or super(Spinner, self).sizeHint()
def _update_viewbox(self, size):
if self._original_viewbox.isEmpty() or size.isEmpty():
return
viewbox = self._original_viewbox.adjusted(self.icon_crop, self.icon_crop, -self.icon_crop, -self.icon_crop)
width = size.width()
height = size.height()
if height >= width:
new_viewbox = QRectF(viewbox.x(), viewbox.y() + viewbox.height()/2 * (1 - height/width), viewbox.width(), viewbox.height() * height/width)
else:
new_viewbox = QRectF(viewbox.x() + viewbox.width()/2 * (1 - width/height), viewbox.y(), viewbox.width() * width/height, viewbox.height())
self.renderer().setViewBox(new_viewbox)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 32 32"
id="svg4146"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="spinner.svg">
<defs
id="defs4148">
<linearGradient
inkscape:collect="always"
id="linearGradient5620">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5622" />
<stop
id="stop5624"
offset="0.85714287"
style="stop-color:#000000;stop-opacity:1" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="0.85714287"
id="stop5626" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop5628" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5620"
id="linearGradient5226"
x1="16"
y1="30"
x2="16"
y2="16"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5620"
id="linearGradient5234"
gradientUnits="userSpaceOnUse"
x1="16"
y1="30"
x2="16"
y2="16"
gradientTransform="translate(0,-32)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="29.1875"
inkscape:cx="16"
inkscape:cy="16"
inkscape:document-units="px"
inkscape:current-layer="g5954"
showgrid="true"
units="px"
inkscape:window-width="1850"
inkscape:window-height="1168"
inkscape:window-x="2"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid4694"
spacingx="0.5"
spacingy="0.5"
empspacing="10" />
</sodipodi:namedview>
<metadata
id="metadata4151">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="g5954"
inkscape:label="Spinner dual color"
style="display:inline;opacity:1">
<circle
r="12.5"
cy="16"
cx="16"
id="circle4307"
style="opacity:0.6;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient5226);stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1" />
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
dur="0.8s" />
<circle
r="12.5"
cy="-16"
cx="16"
style="opacity:0.3;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient5234);stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1"
id="circle4305"
transform="scale(1,-1)" />
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 32 32"
id="svg4146"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="spinner.svg">
<defs
id="defs4148">
<linearGradient
inkscape:collect="always"
id="linearGradient5620">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop5622" />
<stop
id="stop5624"
offset="0.85714287"
style="stop-color:#000000;stop-opacity:1" />
<stop
style="stop-color:#000000;stop-opacity:0"
offset="0.85714287"
id="stop5626" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop5628" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient4255">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4257" />
<stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop4259" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient4255"
id="linearGradient4261"
x1="16"
y1="2"
x2="16"
y2="30"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5620"
id="linearGradient5226"
x1="16"
y1="30"
x2="16"
y2="16"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient5620"
id="linearGradient5234"
gradientUnits="userSpaceOnUse"
x1="16"
y1="30"
x2="16"
y2="16"
gradientTransform="translate(0,-32)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="29.1875"
inkscape:cx="16"
inkscape:cy="16"
inkscape:document-units="px"
inkscape:current-layer="g5954"
showgrid="true"
units="px"
inkscape:window-width="1850"
inkscape:window-height="1168"
inkscape:window-x="2"
inkscape:window-y="0"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true">
<inkscape:grid
type="xygrid"
id="grid4694"
spacingx="0.5"
spacingy="0.5"
empspacing="10" />
</sodipodi:namedview>
<metadata
id="metadata4151">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Spinner"
style="display:none;opacity:1">
<circle
style="opacity:0.2;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1"
id="circle4147"
cx="16"
cy="16"
r="12.5" />
<path
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 16,2 C 8.2857805,2 2,8.2857805 2,16 l 3,0 C 5,9.9071008 9.9071008,5 16,5 22.092899,5 27,9.9071008 27,16 l 3,0 C 30,8.2857805 23.71422,2 16,2 Z"
id="circle6611"
inkscape:connector-curvature="0">
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
dur="0.8s" />
</path>
</g>
<g
inkscape:groupmode="layer"
id="g4236"
inkscape:label="Spinner 180"
style="display:none;opacity:1">
<path
d="M 28.5,16 A 12.5,12.5 0 0 1 22.25,26.825318 12.5,12.5 0 0 1 9.7499997,26.825317 12.5,12.5 0 0 1 3.5,15.999999"
sodipodi:open="true"
sodipodi:end="3.1415927"
sodipodi:start="0"
sodipodi:ry="12.5"
sodipodi:rx="12.5"
sodipodi:cy="16"
sodipodi:cx="16"
sodipodi:type="arc"
style="opacity:0.6;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1"
id="circle4238">
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
dur="0.8s" />
</path>
</g>
<g
style="display:none;opacity:1"
inkscape:label="Spinner 270"
id="g4221"
inkscape:groupmode="layer">
<path
id="circle4223"
style="opacity:0.6;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1"
sodipodi:type="arc"
sodipodi:cx="15.999502"
sodipodi:cy="16.000498"
sodipodi:rx="12.500498"
sodipodi:ry="12.500498"
sodipodi:start="0"
sodipodi:end="4.712389"
sodipodi:open="true"
d="M 28.5,16.000498 A 12.500498,12.500498 0 0 1 20.783236,27.549452 12.500498,12.500498 0 0 1 7.1603153,24.839685 12.500498,12.500498 0 0 1 4.4505482,11.216764 12.500498,12.500498 0 0 1 15.999502,3.5">
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
dur="0.8s" />
</path>
</g>
<g
style="display:none;opacity:1"
inkscape:label="Spinner gradient"
id="g4240"
inkscape:groupmode="layer">
<circle
r="12.5"
cy="16"
cx="16"
id="circle4242"
style="opacity:0.6;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4261);stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1">
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
dur="0.8s" />
</circle>
</g>
<g
inkscape:groupmode="layer"
id="g5954"
inkscape:label="Spinner dual color"
style="display:inline;opacity:1">
<animateTransform
attributeName="transform"
type="rotate"
from="0 16 16"
to="360 16 16"
repeatCount="indefinite"
dur="0.8s" />
<circle
r="12.5"
cy="16"
cx="16"
id="circle4307"
style="opacity:0.6;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient5226);stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1" />
<circle
r="12.5"
cy="-16"
cx="16"
style="opacity:0.3;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient5234);stroke-width:3;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-dashoffset:3.55200005;stroke-opacity:1"
id="circle4305"
transform="scale(1,-1)" />
</g>
</svg>
......@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>700</width>
<height>670</height>
<width>720</width>
<height>730</height>
</rect>
</property>
<property name="windowTitle">
......@@ -21,46 +21,40 @@
<property name="spacing">
<number>0</number>
</property>
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="account_selector" native="true">
<layout class="QHBoxLayout" name="account_layout">
<widget class="QWidget" name="navigation_widget" native="true">
<layout class="QHBoxLayout" name="navigation_layout">
<property name="spacing">
<number>3</number>
<number>6</number>
</property>
<property name="leftMargin">
<number>2</number>
<number>3</number>
</property>
<property name="topMargin">
<number>3</number>
<number>0</number>
</property>
<property name="rightMargin">
<number>3</number>
</property>
<property name="bottomMargin">
<number>3</number>
<number>0</number>
</property>
<item>
<widget class="ToolButton" name="account_button">
<property name="minimumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>40</width>
<height>40</height>
</size>
</property>
<property name="focusPolicy">
<enum>Qt::NoFocus</enum>
</property>
<property name="text">
<string/>
<string>user@domain</string>
</property>
<property name="icon">
<iconset>
......@@ -75,44 +69,29 @@
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextBesideIcon</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="Line" name="separator">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QFrame" name="location_frame">
<widget class="QWidget" name="location_widget" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string>QFrame#location_frame {
background: #2060c0;
border-style: outset;
border-width: 1px;
border-radius: 4px;
border-color: #104080;
margin-top: 4px;
margin-bottom: 4px;
padding-left: 2px;
padding-right: 2px;
color: white;
}
QWidget {
color: white;
}
</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<layout class="QHBoxLayout" name="location_layout">
<property name="spacing">
<number>7</number>
......@@ -124,62 +103,97 @@ QWidget {
<number>0</number>
</property>
<property name="rightMargin">
<number>4</number>
<number>7</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="account_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<widget class="QToolButton" name="back_button">
<property name="text">
<string>Back</string>
</property>
<property name="icon">
<iconset theme="draw-arrow-back">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonFollowStyle</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="forward_button">
<property name="text">
<string>Forward</string>
</property>
<property name="icon">
<iconset theme="draw-arrow-forward">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="popupMode">
<enum>QToolButton::MenuButtonPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonFollowStyle</enum>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progress_bar">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>12</height>
</size>
<widget class="QToolButton" name="home_button">
<property name="text">
<string>Home</string>
</property>
<property name="styleSheet">
<string>QProgressBar {
border: 1px solid white;
border-radius: 2px;
}
QProgressBar::chunk {
background-color: #2060c0;
width: 3px;
margin-right: 1px;
}</string>
<property name="icon">
<iconset theme="go-home">
<normaloff>.</normaloff>.</iconset>
</property>
<property name="value">
<number>30</number>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonFollowStyle</enum>
</property>
<property name="textVisible">
<bool>false</bool>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="spinner_label">
<property name="pixmap">
<pixmap>icons/servertools-spinner.mng</pixmap>
<spacer name="spacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="Spinner" name="spinner" native="true">
<property name="minimumSize">
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="icon_size" stdset="0">
<size>
<width>18</width>
<height>18</height>
</size>
</property>
<property name="icon_crop" stdset="0">
<number>0</number>
</property>
</widget>
</item>
......@@ -190,35 +204,46 @@ QProgressBar::chunk {
</widget>
</item>
<item>
<widget class="QTabWidget" name="tab_widget">
<property name="currentIndex">
<number>0</number>
</property>
<property name="documentMode">
<bool>true</bool>
</property>
<property name="tabsClosable">
<bool>true</bool>
<widget class="ServerToolsWebView" name="web_view">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="movable">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QWebView</class>
<extends>QWidget</extends>
<header>QtWebKitWidgets/QWebView</header>
</customwidget>
<customwidget>
<class>ServerToolsWebView</class>
<extends>QWebView</extends>
<header>blink.accounts</header>
</customwidget>
<customwidget>
<class>ToolButton</class>
<extends>QToolButton</extends>
<header>blink.widgets.buttons</header>
</customwidget>
<customwidget>
<class>Spinner</class>
<extends>QWidget</extends>
<header>blink.widgets.spinner</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>web_view</tabstop>
<tabstop>account_button</tabstop>
<tabstop>back_button</tabstop>
<tabstop>forward_button</tabstop>
<tabstop>home_button</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment