فهرست منبع

UI: Redesign status bar

This redesigns the status bar to look more modern.

Co-authored-by: Warchamp7 <[email protected]>
Co-authored-by: Georges Basile Stavracas Neto <[email protected]>
cg2121 2 سال پیش
والد
کامیت
1c84071830

+ 1 - 0
UI/cmake/legacy.cmake

@@ -116,6 +116,7 @@ target_sources(
           forms/OBSRemux.ui
           forms/OBSUpdate.ui
           forms/OBSYoutubeActions.ui
+          forms/StatusBarWidget.ui
           forms/source-toolbar/browser-source-toolbar.ui
           forms/source-toolbar/color-source-toolbar.ui
           forms/source-toolbar/device-select-toolbar.ui

+ 13 - 1
UI/data/themes/Acri.qss

@@ -337,8 +337,20 @@ QScrollArea {
     border-radius: 4px;
 }
 
+/* Qt enforces a padding inside its status bar, so we
+ * oversize it and use margin to crunch it back down
+ */
 OBSBasicStatusBar {
-    margin-top: 8px;
+    margin-top: 4px;
+    border-top: 1px solid #3c404b;
+    background: palette(dark);
+}
+
+StatusBarWidget > QFrame {
+    margin-top: 1px;
+    border: 0px solid #3c404b;
+    border-left-width: 1px;
+    padding: 0px 12px 2px;
 }
 
 /* Group Box */

+ 16 - 0
UI/data/themes/Dark.qss

@@ -644,6 +644,22 @@ QStatusBar::item {
     border: none;
 }
 
+/* Qt enforces a padding inside its status bar, so we
+ * oversize it and use margin to crunch it back down
+ */
+OBSBasicStatusBar {
+    margin-top: 4px;
+    border-top: 1px solid palette(dark);
+    background: palette(window);
+}
+
+StatusBarWidget > QFrame {
+    margin-top: 2px;
+    border: 0px solid palette(dark);
+    border-left-width: 1px;
+    padding: 0px 12px 4px;
+}
+
 /* Table View */
 
 QTableView {

+ 7 - 0
UI/data/themes/Dark/network-disconnected.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g transform="translate(-2388.3 -3285.1)">
+  <path d="m2401.3 3286.1c-0.5547 0-1 0.4453-1 1v7.2695c0.1485 0.09 0.2891 0.1914 0.4141 0.3164l0.414 0.4141h0.3438l0.4141-0.4141c0.375-0.375 0.8828-0.5859 1.414-0.5859v-7c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v9c0 0.5547 0.4453 1 1 1h0.01c0-0.5156 0.1914-1.0273 0.5781-1.4141l0.4141-0.414v-0.3438l-0.4141-0.414c-0.7734-0.7735-0.7734-2.0547 0-2.8282 0.375-0.375 0.8829-0.5859 1.4141-0.5859v-4c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v6c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-6c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v3c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-3c0-0.5547-0.4453-1-1-1z" fill="#fff" fill-opacity=".2"/>
+  <path d="m2399.3 3295.1c-0.2656 0-0.5195 0.1055-0.707 0.293-0.3906 0.3906-0.3906 1.0234 0 1.414l1.293 1.293-1.293 1.293c-0.3906 0.3906-0.3906 1.0234 0 1.414 0.3906 0.3907 1.0234 0.3907 1.414 0l1.293-1.2929 1.293 1.2929c0.3906 0.3907 1.0234 0.3907 1.414 0 0.3907-0.3906 0.3907-1.0234 0-1.414l-1.2929-1.293 1.2929-1.293c0.3907-0.3906 0.3907-1.0234 0-1.414-0.1875-0.1875-0.4414-0.293-0.707-0.293s-0.5195 0.1055-0.707 0.293l-1.293 1.2929-1.293-1.2929c-0.1875-0.1875-0.4414-0.293-0.707-0.293z" fill="#b8131a"/>
+ </g>
+</svg>

+ 4 - 0
UI/data/themes/Dark/network-inactive.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <path d="m13 1c-0.55469 0-1 0.44531-1 1v12c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-12c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v9c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-9c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v6c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-6c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v3c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-3c0-0.55469-0.44531-1-1-1z" fill="#fff" fill-opacity=".2"/>
+</svg>

+ 4 - 0
UI/data/themes/Dark/recording-inactive.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <path d="m15 8c0 3.8672-3.1328 7-7 7s-7-3.1328-7-7 3.1328-7 7-7 7 3.1328 7 7z" fill="#fff" fill-opacity=".2"/>
+</svg>

+ 7 - 0
UI/data/themes/Dark/recording-pause-inactive.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#fff" fill-opacity=".2">
+  <path d="m3 1h3c0.55078 0 1 0.44922 1 1v12c0 0.55078-0.44922 1-1 1h-3c-0.55078 0-1-0.44922-1-1v-12c0-0.55078 0.44922-1 1-1z"/>
+  <path d="m10 1h3c0.55078 0 1 0.44922 1 1v12c0 0.55078-0.44922 1-1 1h-3c-0.55078 0-1-0.44922-1-1v-12c0-0.55078 0.44922-1 1-1z"/>
+ </g>
+</svg>

+ 20 - 0
UI/data/themes/Dark/streaming-inactive.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <g transform="matrix(1.8565 0 0 1.8565 .152 -547.29)">
+  <circle cx="1.0583" cy="295.94" r=".37705" fill="#fff" fill-opacity=".4" style="paint-order:normal"/>
+  <g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round">
+   <path d="m0.6324 296.42a0.62244 0.62244 0 0 1-0.22234-0.47682 0.62244 0.62244 0 0 1 0.22234-0.47682" stroke-opacity=".22" stroke-width=".18852" style="paint-order:normal"/>
+   <path d="m1.4833 296.42a0.62244 0.62244 0 0 0 0.22234-0.47682 0.62244 0.62244 0 0 0-0.22234-0.47682" stroke-opacity=".22" stroke-width=".18852" style="paint-order:normal"/>
+   <path d="m0.39544 296.7a0.98443 0.98443 0 0 1-0.35165-0.75411 0.98443 0.98443 0 0 1 0.35165-0.75413" stroke-opacity=".08" stroke-width=".25133" style="paint-order:normal"/>
+   <path d="m1.7211 296.7a0.98443 0.98443 0 0 0 0.35165-0.75411 0.98443 0.98443 0 0 0-0.35165-0.75413" stroke-opacity=".08" stroke-width=".25133" style="paint-order:normal"/>
+  </g>
+ </g>
+</svg>

+ 13 - 1
UI/data/themes/Grey.qss

@@ -337,8 +337,20 @@ QScrollArea {
     border-radius: 4px;
 }
 
+/* Qt enforces a padding inside its status bar, so we
+ * oversize it and use margin to crunch it back down
+ */
 OBSBasicStatusBar {
-    margin-top: 8px;
+    margin-top: 4px;
+    border-top: 1px solid #3c404b;
+    background: palette(dark);
+}
+
+StatusBarWidget > QFrame {
+    margin-top: 1px;
+    border: 0px solid #3c404b;
+    border-left-width: 1px;
+    padding: 0px 12px 2px;
 }
 
 /* Group Box */

+ 13 - 1
UI/data/themes/Light.qss

@@ -337,8 +337,20 @@ QScrollArea {
     border-radius: 4px;
 }
 
+/* Qt enforces a padding inside its status bar, so we
+ * oversize it and use margin to crunch it back down
+ */
 OBSBasicStatusBar {
-    margin-top: 8px;
+    margin-top: 4px;
+    border-top: 1px solid rgb(192,192,192);
+    background: palette(dark);
+}
+
+StatusBarWidget > QFrame {
+    margin-top: 1px;
+    border: 0px solid rgb(192,192,192);
+    border-left-width: 1px;
+    padding: 0px 12px 2px;
 }
 
 /* Group Box */

+ 13 - 1
UI/data/themes/Rachni.qss

@@ -339,8 +339,20 @@ QScrollArea {
     border-radius: 4px;
 }
 
+/* Qt enforces a padding inside its status bar, so we
+ * oversize it and use margin to crunch it back down
+ */
 OBSBasicStatusBar {
-    margin-top: 8px;
+    margin-top: 4px;
+    border-top: 1px solid #3c404b;
+    background: palette(dark);
+}
+
+StatusBarWidget > QFrame {
+    margin-top: 1px;
+    border: 0px solid #3c404b;
+    border-left-width: 1px;
+    padding: 0px 12px 2px;
 }
 
 /* Group Box */

+ 5 - 0
UI/data/themes/System.qss

@@ -402,3 +402,8 @@ QCalendarWidget #qt_calendar_nextmonth {
     qproperty-icon: url(./Dark/right.svg);
     icon-size: 16px, 16px;
 }
+
+/* Status Bar */
+StatusBarWidget > QFrame {
+    padding: 0px 12px 8px;
+}

+ 13 - 1
UI/data/themes/Yami.qss

@@ -341,8 +341,20 @@ QScrollArea {
     border-radius: 4px;
 }
 
+/* Qt enforces a padding inside its status bar, so we
+ * oversize it and use margin to crunch it back down
+ */
 OBSBasicStatusBar {
-    margin-top: 8px;
+    margin-top: 4px;
+    border-top: 1px solid #3c404b;
+    background: palette(dark);
+}
+
+StatusBarWidget > QFrame {
+    margin-top: 1px;
+    border: 0px solid #3c404b;
+    border-left-width: 1px;
+    padding: 0px 12px 2px;
 }
 
 /* Group Box */

+ 418 - 0
UI/forms/StatusBarWidget.ui

@@ -0,0 +1,418 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>StatusBarWidget</class>
+ <widget class="QWidget" name="StatusBarWidget">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>714</width>
+    <height>34</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>0</width>
+    <height>34</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>6</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <spacer name="horizontalSpacer_11">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>2</width>
+       <height>2</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <widget class="QFrame" name="delayFrame">
+     <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="horizontalLayout_7">
+      <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="QLabel" name="delayInfo">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>DelayInfo</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="issuesFrame">
+     <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="horizontalLayout_6">
+      <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="QLabel" name="droppedFrames">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>DroppedFrames</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="networkFrame">
+     <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="horizontalLayout_4">
+      <property name="spacing">
+       <number>6</number>
+      </property>
+      <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="QLabel" name="statusIcon">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+        <property name="pixmap">
+         <pixmap resource="obs.qrc">:/res/images/network-inactive.svg</pixmap>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="kbps">
+        <property name="enabled">
+         <bool>true</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>0 kbps</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="streamFrame">
+     <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="horizontalLayout_3">
+      <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="QLabel" name="streamIcon">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+        <property name="pixmap">
+         <pixmap resource="obs.qrc">:/res/images/streaming-inactive.svg</pixmap>
+        </property>
+        <property name="scaledContents">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="streamTime">
+        <property name="enabled">
+         <bool>true</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>00:00:00</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="recordFrame">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </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="horizontalLayout_2">
+      <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="QLabel" name="recordIcon">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string/>
+        </property>
+        <property name="pixmap">
+         <pixmap resource="obs.qrc">:/res/images/recording-inactive.svg</pixmap>
+        </property>
+        <property name="scaledContents">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QLabel" name="recordTime">
+        <property name="enabled">
+         <bool>true</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>00:00:00</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="cpuFrame">
+     <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="horizontalLayout_5">
+      <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="QLabel" name="cpuUsage">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>CPU: 0.0%</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignCenter</set>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QFrame" name="fpsFrame">
+     <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="horizontalLayout_8">
+      <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="QLabel" name="fpsCurrent">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Maximum" vsizetype="Maximum">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="text">
+         <string>0.00 / 0.00 FPS</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignCenter</set>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="obs.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 7 - 0
UI/forms/images/network-bad.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#b8131a">
+  <path d="m1 10c-0.55469 0-1 0.44531-1 1v3c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-3c0-0.55469-0.44531-1-1-1z"/>
+  <path d="m13 1c-0.55469 0-1 0.44531-1 1v12c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-12c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v9c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-9c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v6c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-6c0-0.55469-0.44531-1-1-1z" fill-opacity=".30196"/>
+ </g>
+</svg>

+ 7 - 0
UI/forms/images/network-disconnected.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g transform="translate(-2388.3 -3285.1)">
+  <path d="m2401.3 3286.1c-0.5547 0-1 0.4453-1 1v7.2695c0.1485 0.09 0.2891 0.1914 0.4141 0.3164l0.414 0.4141h0.3438l0.4141-0.4141c0.375-0.375 0.8828-0.5859 1.414-0.5859v-7c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v9c0 0.5547 0.4453 1 1 1h0.01c0-0.5156 0.1914-1.0273 0.5781-1.4141l0.4141-0.414v-0.3438l-0.4141-0.414c-0.7734-0.7735-0.7734-2.0547 0-2.8282 0.375-0.375 0.8829-0.5859 1.4141-0.5859v-4c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v6c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-6c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v3c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-3c0-0.5547-0.4453-1-1-1z" fill-opacity=".1"/>
+  <path d="m2399.3 3295.1c-0.2656 0-0.5195 0.1055-0.707 0.293-0.3906 0.3906-0.3906 1.0234 0 1.414l1.293 1.293-1.293 1.293c-0.3906 0.3906-0.3906 1.0234 0 1.414 0.3906 0.3907 1.0234 0.3907 1.414 0l1.293-1.2929 1.293 1.2929c0.3906 0.3907 1.0234 0.3907 1.414 0 0.3907-0.3906 0.3907-1.0234 0-1.414l-1.2929-1.293 1.2929-1.293c0.3907-0.3906 0.3907-1.0234 0-1.414-0.1875-0.1875-0.4414-0.293-0.707-0.293s-0.5195 0.1055-0.707 0.293l-1.293 1.2929-1.293-1.2929c-0.1875-0.1875-0.4414-0.293-0.707-0.293z" fill="#b8131a"/>
+ </g>
+</svg>

+ 4 - 0
UI/forms/images/network-excellent.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <path d="m13 1c-0.5547 0-1 0.4453-1 1v12c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-12c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v9c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-9c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v6c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-6c0-0.5547-0.4453-1-1-1zm-4 3c-0.5547 0-1 0.4453-1 1v3c0 0.5547 0.4453 1 1 1h1c0.5547 0 1-0.4453 1-1v-3c0-0.5547-0.4453-1-1-1z" fill="#2bc62f"/>
+</svg>

+ 7 - 0
UI/forms/images/network-good.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#49b37a">
+  <path d="m9 4c-0.55469 0-1 0.44531-1 1v9c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-9c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v6c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-6c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v3c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-3c0-0.55469-0.44531-1-1-1z"/>
+  <path d="m13 1c-0.55469 0-1 0.44531-1 1v12c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-12c0-0.55469-0.44531-1-1-1z" fill-opacity=".30196"/>
+ </g>
+</svg>

+ 4 - 0
UI/forms/images/network-inactive.svg

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <path d="m13 1c-0.55469 0-1 0.44531-1 1v12c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-12c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v9c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-9c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v6c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-6c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v3c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-3c0-0.55469-0.44531-1-1-1z" fill-opacity=".15"/>
+</svg>

+ 7 - 0
UI/forms/images/network-mediocre.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#e4ac3c">
+  <path d="m5 7c-0.55469 0-1 0.44531-1 1v6c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-6c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v3c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-3c0-0.55469-0.44531-1-1-1z"/>
+  <path d="m13 1c-0.55469 0-1 0.44531-1 1v12c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-12c0-0.55469-0.44531-1-1-1zm-4 3c-0.55469 0-1 0.44531-1 1v9c0 0.55469 0.44531 1 1 1h1c0.55469 0 1-0.44531 1-1v-9c0-0.55469-0.44531-1-1-1z" fill-opacity=".3"/>
+ </g>
+</svg>

+ 3 - 36
UI/forms/images/recording-active.svg

@@ -1,37 +1,4 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<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"
-   id="svg8"
-   version="1.1"
-   viewBox="0 0 2.1166666 2.1166667"
-   height="8"
-   width="8">
-  <defs
-     id="defs2" />
-  <metadata
-     id="metadata5">
-    <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
-     transform="translate(0,-294.88332)"
-     id="layer1">
-    <circle
-       r="0.52916664"
-       cy="295.94165"
-       cx="1.0583333"
-       id="path4544"
-       style="fill:#c01c28;fill-opacity:1;stroke:none;stroke-width:0.06614584;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-  </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <path d="m15 8c0 3.8672-3.1328 7-7 7s-7-3.1328-7-7 3.1328-7 7-7 7 3.1328 7 7z" fill="#c31212"/>
 </svg>

+ 3 - 13
UI/forms/images/recording-inactive.svg

@@ -1,14 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1"
-	 id="svg8" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" 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" x="0px" y="0px" viewBox="0 0 8 8"
-	 style="enable-background:new 0 0 8 8;" xml:space="preserve">
-<style type="text/css">
-	.st0{fill:#9a9996;}
-</style>
-<g>
-	<path class="st0" d="M5.8,1.5L5,2.3C4.7,2.1,4.4,2,4,2C2.9,2,2,2.9,2,4c0,0.4,0.1,0.7,0.3,1L1.5,5.8l0.4,0.4l4.2-4.2L5.8,1.5z"/>
-	<path class="st0" d="M3,5.7C3.3,5.9,3.6,6,4,6c1.1,0,2-0.9,2-2c0-0.4-0.1-0.7-0.3-1L3,5.7z"/>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <path d="m15 8c0 3.8672-3.1328 7-7 7s-7-3.1328-7-7 3.1328-7 7-7 7 3.1328 7 7z" fill-opacity=".1"/>
 </svg>

+ 6 - 44
UI/forms/images/recording-pause-inactive.svg

@@ -1,45 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<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"
-   id="svg8"
-   version="1.1"
-   viewBox="0 0 5.2916665 5.2916668"
-   height="20"
-   width="20">
-  <defs
-     id="defs2" />
-  <metadata
-     id="metadata5">
-    <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
-     transform="translate(0,-291.70832)"
-     id="layer1">
-    <rect
-       y="293.03125"
-       x="1.3229166"
-       height="2.6458333"
-       width="1.0583333"
-       id="rect822"
-       style="fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:0.5020116;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-    <rect
-       y="293.03125"
-       x="2.9104166"
-       height="2.6458333"
-       width="1.0583333"
-       id="rect822-1"
-       style="fill:#9a9996;fill-opacity:1;stroke:none;stroke-width:0.5020116;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-  </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g fill-opacity=".1">
+  <path d="m3 1h3c0.55078 0 1 0.44922 1 1v12c0 0.55078-0.44922 1-1 1h-3c-0.55078 0-1-0.44922-1-1v-12c0-0.55078 0.44922-1 1-1z"/>
+  <path d="m10 1h3c0.55078 0 1 0.44922 1 1v12c0 0.55078-0.44922 1-1 1h-3c-0.55078 0-1-0.44922-1-1v-12c0-0.55078 0.44922-1 1-1z"/>
+ </g>
 </svg>

+ 6 - 44
UI/forms/images/recording-pause.svg

@@ -1,45 +1,7 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<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"
-   id="svg8"
-   version="1.1"
-   viewBox="0 0 5.2916665 5.2916668"
-   height="20"
-   width="20">
-  <defs
-     id="defs2" />
-  <metadata
-     id="metadata5">
-    <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
-     transform="translate(0,-291.70832)"
-     id="layer1">
-    <rect
-       y="293.03125"
-       x="1.3229166"
-       height="2.6458333"
-       width="1.0583333"
-       id="rect822"
-       style="fill:#f5c211;fill-opacity:1;stroke:none;stroke-width:0.5020116;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-    <rect
-       y="293.03125"
-       x="2.9104166"
-       height="2.6458333"
-       width="1.0583333"
-       id="rect822-1"
-       style="fill:#f5c211;fill-opacity:1;stroke:none;stroke-width:0.5020116;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-  </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16px" height="16px" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+ <g fill="#e5a50a">
+  <path d="m3 1h3c0.55078 0 1 0.44922 1 1v12c0 0.55078-0.44922 1-1 1h-3c-0.55078 0-1-0.44922-1-1v-12c0-0.55078 0.44922-1 1-1z"/>
+  <path d="m10 1h3c0.55078 0 1 0.44922 1 1v12c0 0.55078-0.44922 1-1 1h-3c-0.55078 0-1-0.44922-1-1v-12c0-0.55078 0.44922-1 1-1z"/>
+ </g>
 </svg>

+ 18 - 53
UI/forms/images/streaming-active.svg

@@ -1,55 +1,20 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<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"
-   id="svg8"
-   version="1.1"
-   viewBox="0 0 2.1166666 2.1166667"
-   height="8"
-   width="8">
-  <defs
-     id="defs2" />
-  <metadata
-     id="metadata5">
-    <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
-     transform="translate(0,-294.88332)"
-     id="layer1">
-    <circle
-       r="0.39687499"
-       cy="295.94165"
-       cx="1.0582843"
-       id="path4544-3"
-       style="fill:#1c71d8;fill-opacity:1;stroke:none;stroke-width:0.13229169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-    <path
-       d="M 0.41577997,296.5497 A 0.79374999,0.79374999 0 0 1 0.13224262,295.94165 0.79374999,0.79374999 0 0 1 0.41577996,295.3336"
-       id="path4546-7"
-       style="fill:none;fill-opacity:1;stroke:#1c71d8;stroke-width:0.15875001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-    <path
-       transform="scale(-1,1)"
-       d="m -1.7008867,296.5497 a 0.79374999,0.79374999 0 0 1 -0.2835374,-0.60805 0.79374999,0.79374999 0 0 1 0.2835374,-0.60805"
-       id="path4546-1-3"
-       style="fill:none;fill-opacity:1;stroke:#1c71d8;stroke-width:0.15875001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-    <path
-       d="M 0.63239977,296.41847 A 0.62244385,0.62244385 0 0 1 0.4100551,295.94165 0.62244385,0.62244385 0 0 1 0.63239977,295.46483"
-       id="path4546-5-7"
-       style="fill:none;fill-opacity:1;stroke:#1c71d8;stroke-width:0.15875001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
-    <path
-       transform="scale(-1,1)"
-       d="m -1.484193,296.41847 a 0.62244391,0.62244391 0 0 1 -0.2223447,-0.47682 0.62244391,0.62244391 0 0 1 0.2223447,-0.47682"
-       id="path4546-5-4-2"
-       style="fill:none;fill-opacity:1;stroke:#1c71d8;stroke-width:0.15875001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <g transform="matrix(1.8565 0 0 1.8565 .152 -547.29)">
+  <circle cx="1.0583" cy="295.94" r=".37705" fill="#2f95ff" style="paint-order:normal"/>
+  <g fill="none" stroke="#2f95ff" stroke-linecap="round" stroke-linejoin="round">
+   <path d="m0.6324 296.42a0.62244 0.62244 0 0 1-0.22234-0.47682 0.62244 0.62244 0 0 1 0.22234-0.47682" stroke-opacity=".67" stroke-width=".18852" style="paint-order:normal"/>
+   <path d="m1.4833 296.42a0.62244 0.62244 0 0 0 0.22234-0.47682 0.62244 0.62244 0 0 0-0.22234-0.47682" stroke-opacity=".67" stroke-width=".18852" style="paint-order:normal"/>
+   <path d="m0.39544 296.7a0.98443 0.98443 0 0 1-0.35165-0.75411 0.98443 0.98443 0 0 1 0.35165-0.75413" stroke-opacity=".33" stroke-width=".25133" style="paint-order:normal"/>
+   <path d="m1.7211 296.7a0.98443 0.98443 0 0 0 0.35165-0.75411 0.98443 0.98443 0 0 0-0.35165-0.75413" stroke-opacity=".33" stroke-width=".25133" style="paint-order:normal"/>
   </g>
+ </g>
 </svg>

+ 19 - 20
UI/forms/images/streaming-inactive.svg

@@ -1,21 +1,20 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 25.2.3, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-<svg version="1.1"
-	 id="svg8" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" 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" x="0px" y="0px" viewBox="0 0 8 8"
-	 style="enable-background:new 0 0 8 8;" xml:space="preserve">
-<style type="text/css">
-	.st0{fill:#9a9996;}
-</style>
-<g>
-	<path class="st0" d="M6.5,0.8L4.6,2.6C4.4,2.6,4.2,2.5,4,2.5C3.2,2.5,2.5,3.2,2.5,4c0,0.2,0.1,0.4,0.1,0.6L2.2,5.1
-		C2,4.8,1.8,4.4,1.8,4c0-0.6,0.3-1.2,0.7-1.6c0.1-0.1,0.1-0.3,0-0.4C2.5,1.9,2.3,1.9,2.2,2C1.6,2.5,1.2,3.2,1.2,4
-		c0,0.6,0.2,1.1,0.5,1.5L1.5,5.8C1.1,5.3,0.8,4.7,0.8,4c0-0.8,0.4-1.6,1-2.1c0.1-0.1,0.1-0.3,0-0.4c-0.1-0.1-0.3-0.1-0.4,0
-		C0.6,2.1,0.2,3,0.2,4c0,0.8,0.3,1.6,0.9,2.2L0.8,6.5l0.4,0.4l5.7-5.7L6.5,0.8z"/>
-	<path class="st0" d="M5.4,3.4l-2,2C3.6,5.4,3.8,5.5,4,5.5c0.8,0,1.5-0.7,1.5-1.5C5.5,3.8,5.4,3.6,5.4,3.4z"/>
-	<path class="st0" d="M6.2,2.5L5.8,2.9C6,3.2,6.2,3.6,6.2,4c0,0.6-0.3,1.2-0.7,1.6C5.3,5.7,5.3,5.9,5.4,6c0.1,0.1,0.1,0.1,0.2,0.1
-		c0.1,0,0.1,0,0.2-0.1c0.6-0.5,0.9-1.2,0.9-2C6.8,3.4,6.6,2.9,6.2,2.5z"/>
-	<path class="st0" d="M6.9,1.8L6.5,2.2C6.9,2.7,7.2,3.3,7.2,4c0,0.8-0.4,1.6-1,2.1c-0.1,0.1-0.1,0.3,0,0.4c0.1,0.1,0.1,0.1,0.2,0.1
-		c0.1,0,0.1,0,0.2-0.1C7.4,5.9,7.8,5,7.8,4C7.8,3.2,7.5,2.4,6.9,1.8z"/>
-</g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="16" height="16" version="1.1" viewBox="0 0 4.2333 4.2333" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata>
+  <rdf:RDF>
+   <cc:Work rdf:about="">
+    <dc:format>image/svg+xml</dc:format>
+    <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+   </cc:Work>
+  </rdf:RDF>
+ </metadata>
+ <g transform="matrix(1.8565 0 0 1.8565 .152 -547.29)">
+  <circle cx="1.0583" cy="295.94" r=".37705" fill-opacity=".18" style="paint-order:normal"/>
+  <g fill="none" stroke-linecap="round" stroke-linejoin="round">
+   <path d="m0.6324 296.42a0.62244 0.62244 0 0 1-0.22234-0.47682 0.62244 0.62244 0 0 1 0.22234-0.47682" stroke="#000" stroke-opacity=".12" stroke-width=".18852" style="paint-order:normal"/>
+   <path d="m1.4833 296.42a0.62244 0.62244 0 0 0 0.22234-0.47682 0.62244 0.62244 0 0 0-0.22234-0.47682" stroke="#000" stroke-opacity=".12" stroke-width=".18852" style="paint-order:normal"/>
+   <path d="m0.39544 296.7a0.98443 0.98443 0 0 1-0.35165-0.75411 0.98443 0.98443 0 0 1 0.35165-0.75413" stroke="#060606" stroke-opacity=".07" stroke-width=".25133" style="paint-order:normal"/>
+   <path d="m1.7211 296.7a0.98443 0.98443 0 0 0 0.35165-0.75411 0.98443 0.98443 0 0 0-0.35165-0.75413" stroke="#060606" stroke-opacity=".07" stroke-width=".25133" style="paint-order:normal"/>
+  </g>
+ </g>
 </svg>

+ 6 - 0
UI/forms/obs.qrc

@@ -64,6 +64,12 @@
     <file>images/expand.svg</file>
     <file>images/collapse.svg</file>
     <file>images/entry-clear.svg</file>
+    <file>images/network-excellent.svg</file>
+    <file>images/network-good.svg</file>
+    <file>images/network-mediocre.svg</file>
+    <file>images/network-bad.svg</file>
+    <file>images/network-disconnected.svg</file>
+    <file>images/network-inactive.svg</file>
   </qresource>
   <qresource prefix="/settings">
     <file>images/settings/output.svg</file>

+ 218 - 138
UI/window-basic-status-bar.cpp

@@ -1,97 +1,66 @@
-#include <QLabel>
-#include <QHBoxLayout>
 #include <QPainter>
 #include <QPixmap>
 #include "obs-app.hpp"
 #include "window-basic-main.hpp"
 #include "window-basic-status-bar.hpp"
 #include "window-basic-main-outputs.hpp"
+#include "qt-wrappers.hpp"
+#include "platform.hpp"
+
+#include "ui_StatusBarWidget.h"
+
+static constexpr int bitrateUpdateSeconds = 2;
+static constexpr int congestionUpdateSeconds = 4;
+static constexpr float excellentThreshold = 0.0f;
+static constexpr float goodThreshold = 0.3333f;
+static constexpr float mediocreThreshold = 0.6667f;
+static constexpr float badThreshold = 1.0f;
+
+StatusBarWidget::StatusBarWidget(QWidget *parent)
+	: QWidget(parent),
+	  ui(new Ui::StatusBarWidget)
+{
+	ui->setupUi(this);
+}
+
+StatusBarWidget::~StatusBarWidget() {}
 
 OBSBasicStatusBar::OBSBasicStatusBar(QWidget *parent)
 	: QStatusBar(parent),
-	  delayInfo(new QLabel),
-	  droppedFrames(new QLabel),
-	  streamIcon(new QLabel),
-	  streamTime(new QLabel),
-	  recordTime(new QLabel),
-	  recordIcon(new QLabel),
-	  cpuUsage(new QLabel),
-	  transparentPixmap(20, 20),
-	  greenPixmap(20, 20),
-	  grayPixmap(20, 20),
-	  redPixmap(20, 20),
+	  excellentPixmap(QIcon(":/res/images/network-excellent.svg")
+				  .pixmap(QSize(16, 16))),
+	  goodPixmap(
+		  QIcon(":/res/images/network-good.svg").pixmap(QSize(16, 16))),
+	  mediocrePixmap(QIcon(":/res/images/network-mediocre.svg")
+				 .pixmap(QSize(16, 16))),
+	  badPixmap(
+		  QIcon(":/res/images/network-bad.svg").pixmap(QSize(16, 16))),
 	  recordingActivePixmap(QIcon(":/res/images/recording-active.svg")
-					.pixmap(QSize(20, 20))),
+					.pixmap(QSize(16, 16))),
 	  recordingPausePixmap(QIcon(":/res/images/recording-pause.svg")
-				       .pixmap(QSize(20, 20))),
-	  recordingPauseInactivePixmap(
-		  QIcon(":/res/images/recording-pause-inactive.svg")
-			  .pixmap(QSize(20, 20))),
-	  recordingInactivePixmap(QIcon(":/res/images/recording-inactive.svg")
-					  .pixmap(QSize(20, 20))),
+				       .pixmap(QSize(16, 16))),
 	  streamingActivePixmap(QIcon(":/res/images/streaming-active.svg")
-					.pixmap(QSize(20, 20))),
-	  streamingInactivePixmap(QIcon(":/res/images/streaming-inactive.svg")
-					  .pixmap(QSize(20, 20)))
+					.pixmap(QSize(16, 16)))
 {
-	streamTime->setText(QString("LIVE: 00:00:00"));
-	recordTime->setText(QString("REC: 00:00:00"));
-	cpuUsage->setText(QString("CPU: 0.0%, 0.00 fps"));
-
-	streamIcon->setPixmap(streamingInactivePixmap);
-	recordIcon->setPixmap(recordingInactivePixmap);
-
-	QWidget *brWidget = new QWidget(this);
-	QHBoxLayout *brLayout = new QHBoxLayout(brWidget);
-	brLayout->setContentsMargins(0, 0, 0, 0);
-
-	statusSquare = new QLabel(brWidget);
-	brLayout->addWidget(statusSquare);
-
-	kbps = new QLabel(brWidget);
-	brLayout->addWidget(kbps);
-
-	brWidget->setLayout(brLayout);
-
-	delayInfo->setAlignment(Qt::AlignRight);
-	delayInfo->setAlignment(Qt::AlignVCenter);
-	droppedFrames->setAlignment(Qt::AlignRight);
-	droppedFrames->setAlignment(Qt::AlignVCenter);
-	streamIcon->setAlignment(Qt::AlignRight);
-	streamIcon->setAlignment(Qt::AlignVCenter);
-	streamTime->setAlignment(Qt::AlignRight);
-	streamTime->setAlignment(Qt::AlignVCenter);
-	recordIcon->setAlignment(Qt::AlignRight);
-	recordIcon->setAlignment(Qt::AlignVCenter);
-	recordTime->setAlignment(Qt::AlignRight);
-	recordTime->setAlignment(Qt::AlignVCenter);
-	cpuUsage->setAlignment(Qt::AlignRight);
-	cpuUsage->setAlignment(Qt::AlignVCenter);
-	kbps->setAlignment(Qt::AlignRight);
-	kbps->setAlignment(Qt::AlignVCenter);
-
-	delayInfo->setIndent(20);
-	droppedFrames->setIndent(20);
-	streamIcon->setIndent(20);
-	recordIcon->setIndent(20);
-	cpuUsage->setIndent(20);
-	kbps->setIndent(10);
-
-	addPermanentWidget(droppedFrames);
-	addPermanentWidget(streamIcon);
-	addPermanentWidget(streamTime);
-	addPermanentWidget(recordIcon);
-	addPermanentWidget(recordTime);
-	addPermanentWidget(cpuUsage);
-	addPermanentWidget(delayInfo);
-	addPermanentWidget(brWidget);
-
-	transparentPixmap.fill(QColor(0, 0, 0, 0));
-	greenPixmap.fill(QColor(0, 255, 0));
-	grayPixmap.fill(QColor(72, 72, 72));
-	redPixmap.fill(QColor(255, 0, 0));
-
-	statusSquare->setPixmap(transparentPixmap);
+	statusWidget = new StatusBarWidget(this);
+	statusWidget->ui->delayInfo->setText("");
+	statusWidget->ui->droppedFrames->setText(
+		QTStr("DroppedFrames").arg("0", "0.0"));
+	statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
+	statusWidget->ui->streamIcon->setPixmap(streamingInactivePixmap);
+	statusWidget->ui->streamTime->setDisabled(true);
+	statusWidget->ui->recordIcon->setPixmap(recordingInactivePixmap);
+	statusWidget->ui->recordTime->setDisabled(true);
+	statusWidget->ui->delayFrame->hide();
+	statusWidget->ui->issuesFrame->hide();
+	statusWidget->ui->kbps->hide();
+
+	addPermanentWidget(statusWidget);
+	setMinimumHeight(statusWidget->height());
+
+	UpdateIcons();
+	connect(App(), &OBSApp::StyleChanged, this,
+		&OBSBasicStatusBar::UpdateIcons);
 }
 
 void OBSBasicStatusBar::Activate()
@@ -114,16 +83,21 @@ void OBSBasicStatusBar::Activate()
 		active = true;
 
 		if (streamOutput) {
-			statusSquare->setPixmap(grayPixmap);
+			statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
 		}
 	}
 
 	if (streamOutput) {
-		streamIcon->setPixmap(streamingActivePixmap);
+		statusWidget->ui->streamIcon->setPixmap(streamingActivePixmap);
+		statusWidget->ui->streamTime->setDisabled(false);
+		statusWidget->ui->issuesFrame->show();
+		statusWidget->ui->kbps->show();
+		firstCongestionUpdate = true;
 	}
 
 	if (recordOutput) {
-		recordIcon->setPixmap(recordingActivePixmap);
+		statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
+		statusWidget->ui->recordTime->setDisabled(false);
 	}
 }
 
@@ -134,23 +108,35 @@ void OBSBasicStatusBar::Deactivate()
 		return;
 
 	if (!streamOutput) {
-		streamTime->setText(QString("LIVE: 00:00:00"));
-		streamIcon->setPixmap(streamingInactivePixmap);
+		statusWidget->ui->streamTime->setText(QString("00:00:00"));
+		statusWidget->ui->streamTime->setDisabled(true);
+		statusWidget->ui->streamIcon->setPixmap(
+			streamingInactivePixmap);
+		statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
+		statusWidget->ui->delayFrame->hide();
+		statusWidget->ui->issuesFrame->hide();
+		statusWidget->ui->kbps->hide();
 		totalStreamSeconds = 0;
+		congestionArray.clear();
+		disconnected = false;
+		firstCongestionUpdate = false;
 	}
 
 	if (!recordOutput) {
-		recordTime->setText(QString("REC: 00:00:00"));
-		recordIcon->setPixmap(recordingInactivePixmap);
+		statusWidget->ui->recordTime->setText(QString("00:00:00"));
+		statusWidget->ui->recordTime->setDisabled(true);
+		statusWidget->ui->recordIcon->setPixmap(
+			recordingInactivePixmap);
 		totalRecordSeconds = 0;
 	}
 
 	if (main->outputHandler && !main->outputHandler->Active()) {
 		delete refreshTimer;
 
-		delayInfo->setText("");
-		droppedFrames->setText("");
-		kbps->setText("");
+		statusWidget->ui->delayInfo->setText("");
+		statusWidget->ui->droppedFrames->setText(
+			QTStr("DroppedFrames").arg("0", "0.0"));
+		statusWidget->ui->kbps->setText("0 kbps");
 
 		delaySecTotal = 0;
 		delaySecStarting = 0;
@@ -159,7 +145,7 @@ void OBSBasicStatusBar::Deactivate()
 		active = false;
 		overloadedNotify = true;
 
-		statusSquare->setPixmap(transparentPixmap);
+		statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
 	}
 }
 
@@ -184,19 +170,20 @@ void OBSBasicStatusBar::UpdateDelayMsg()
 			msg = QTStr("Basic.StatusBar.Delay");
 			msg = msg.arg(QString::number(delaySecTotal));
 		}
-	}
 
-	delayInfo->setText(msg);
-}
+		if (!statusWidget->ui->delayFrame->isVisible())
+			statusWidget->ui->delayFrame->show();
 
-#define BITRATE_UPDATE_SECONDS 2
+		statusWidget->ui->delayInfo->setText(msg);
+	}
+}
 
 void OBSBasicStatusBar::UpdateBandwidth()
 {
 	if (!streamOutput)
 		return;
 
-	if (++bitrateUpdateSeconds < BITRATE_UPDATE_SECONDS)
+	if (++seconds < bitrateUpdateSeconds)
 		return;
 
 	uint64_t bytesSent = obs_output_get_total_bytes(streamOutput);
@@ -215,14 +202,18 @@ void OBSBasicStatusBar::UpdateBandwidth()
 	double kbitsPerSec = double(bitsBetween) / timePassed / 1000.0;
 
 	QString text;
-	text += QString("kb/s: ") + QString::number(kbitsPerSec, 'f', 0);
+	text += QString::number(kbitsPerSec, 'f', 0) + QString(" kbps");
+
+	statusWidget->ui->kbps->setText(text);
+	statusWidget->ui->kbps->setMinimumWidth(
+		statusWidget->ui->kbps->width());
 
-	kbps->setText(text);
-	kbps->setMinimumWidth(kbps->width());
+	if (!statusWidget->ui->kbps->isVisible())
+		statusWidget->ui->kbps->show();
 
 	lastBytesSent = bytesSent;
 	lastBytesSentTime = bytesSentTime;
-	bitrateUpdateSeconds = 0;
+	seconds = 0;
 }
 
 void OBSBasicStatusBar::UpdateCPUUsage()
@@ -233,11 +224,34 @@ void OBSBasicStatusBar::UpdateCPUUsage()
 
 	QString text;
 	text += QString("CPU: ") +
-		QString::number(main->GetCPUUsage(), 'f', 1) + QString("%, ") +
-		QString::number(obs_get_active_fps(), 'f', 2) + QString(" fps");
+		QString::number(main->GetCPUUsage(), 'f', 1) + QString("%");
+
+	statusWidget->ui->cpuUsage->setText(text);
+	statusWidget->ui->cpuUsage->setMinimumWidth(
+		statusWidget->ui->cpuUsage->width());
 
-	cpuUsage->setText(text);
-	cpuUsage->setMinimumWidth(cpuUsage->width());
+	UpdateCurrentFPS();
+}
+
+void OBSBasicStatusBar::UpdateCurrentFPS()
+{
+	OBSBasic *main = qobject_cast<OBSBasic *>(parent());
+	if (!main)
+		return;
+
+	struct obs_video_info ovi;
+	obs_get_video_info(&ovi);
+	float targetFPS = (float)ovi.fps_num / (float)ovi.fps_den;
+
+	QString text;
+	text += QString::number(obs_get_active_fps(), 'f', 2);
+	text += QString(" / ");
+	text += QString::number(targetFPS, 'f', 2);
+	text += QString(" FPS");
+
+	statusWidget->ui->fpsCurrent->setText(text);
+	statusWidget->ui->fpsCurrent->setMinimumWidth(
+		statusWidget->ui->fpsCurrent->width());
 }
 
 void OBSBasicStatusBar::UpdateStreamTime()
@@ -249,16 +263,20 @@ void OBSBasicStatusBar::UpdateStreamTime()
 	int minutes = totalMinutes % 60;
 	int hours = totalMinutes / 60;
 
-	QString text = QString::asprintf("LIVE: %02d:%02d:%02d", hours, minutes,
-					 seconds);
-	streamTime->setText(text);
-	streamTime->setMinimumWidth(streamTime->width());
+	QString text =
+		QString::asprintf("%02d:%02d:%02d", hours, minutes, seconds);
+	statusWidget->ui->streamTime->setText(text);
+	if (streamOutput && !statusWidget->ui->streamTime->isEnabled())
+		statusWidget->ui->streamTime->setDisabled(false);
 
 	if (reconnectTimeout > 0) {
 		QString msg = QTStr("Basic.StatusBar.Reconnecting")
 				      .arg(QString::number(retries),
 					   QString::number(reconnectTimeout));
 		showMessage(msg);
+		disconnected = true;
+		statusWidget->ui->statusIcon->setPixmap(disconnectedPixmap);
+		congestionArray.clear();
 		reconnectTimeout--;
 
 	} else if (retries > 0) {
@@ -289,14 +307,15 @@ void OBSBasicStatusBar::UpdateRecordTime()
 		int minutes = totalMinutes % 60;
 		int hours = totalMinutes / 60;
 
-		QString text = QString::asprintf("REC: %02d:%02d:%02d", hours,
+		QString text = QString::asprintf("%02d:%02d:%02d", hours,
 						 minutes, seconds);
 
-		recordTime->setText(text);
-		recordTime->setMinimumWidth(recordTime->width());
+		statusWidget->ui->recordTime->setText(text);
+		if (recordOutput && !statusWidget->ui->recordTime->isEnabled())
+			statusWidget->ui->recordTime->setDisabled(false);
 	} else {
-		recordIcon->setPixmap(streamPauseIconToggle
-					      ? recordingPauseInactivePixmap
+		statusWidget->ui->recordIcon->setPixmap(
+			streamPauseIconToggle ? recordingPauseInactivePixmap
 					      : recordingPausePixmap);
 
 		streamPauseIconToggle = !streamPauseIconToggle;
@@ -318,8 +337,10 @@ void OBSBasicStatusBar::UpdateDroppedFrames()
 	QString text = QTStr("DroppedFrames");
 	text = text.arg(QString::number(totalDropped),
 			QString::number(percent, 'f', 1));
-	droppedFrames->setText(text);
-	droppedFrames->setMinimumWidth(droppedFrames->width());
+	statusWidget->ui->droppedFrames->setText(text);
+
+	if (!statusWidget->ui->issuesFrame->isVisible())
+		statusWidget->ui->issuesFrame->show();
 
 	/* ----------------------------------- *
 	 * calculate congestion color          */
@@ -331,28 +352,37 @@ void OBSBasicStatusBar::UpdateDroppedFrames()
 	if (avgCongestion > 1.0f)
 		avgCongestion = 1.0f;
 
-	if (avgCongestion < EPSILON) {
-		statusSquare->setPixmap(greenPixmap);
-	} else if (fabsf(avgCongestion - 1.0f) < EPSILON) {
-		statusSquare->setPixmap(redPixmap);
-	} else {
-		QPixmap pixmap(20, 20);
+	lastCongestion = congestion;
 
-		float red = avgCongestion * 2.0f;
-		if (red > 1.0f)
-			red = 1.0f;
-		red *= 255.0;
+	if (disconnected)
+		return;
 
-		float green = (1.0f - avgCongestion) * 2.0f;
-		if (green > 1.0f)
-			green = 1.0f;
-		green *= 255.0;
+	bool update = firstCongestionUpdate;
+	float congestionOverTime = avgCongestion;
 
-		pixmap.fill(QColor(int(red), int(green), 0));
-		statusSquare->setPixmap(pixmap);
+	if (congestionArray.size() >= congestionUpdateSeconds) {
+		congestionOverTime = accumulate(congestionArray.begin(),
+						congestionArray.end(), 0.0f) /
+				     (float)congestionArray.size();
+		congestionArray.clear();
+		update = true;
+	} else {
+		congestionArray.emplace_back(avgCongestion);
 	}
 
-	lastCongestion = congestion;
+	if (update) {
+		if (congestionOverTime <= excellentThreshold + EPSILON)
+			statusWidget->ui->statusIcon->setPixmap(
+				excellentPixmap);
+		else if (congestionOverTime <= goodThreshold)
+			statusWidget->ui->statusIcon->setPixmap(goodPixmap);
+		else if (congestionOverTime <= mediocreThreshold)
+			statusWidget->ui->statusIcon->setPixmap(mediocrePixmap);
+		else if (congestionOverTime <= badThreshold)
+			statusWidget->ui->statusIcon->setPixmap(badPixmap);
+
+		firstCongestionUpdate = false;
+	}
 }
 
 void OBSBasicStatusBar::OBSOutputReconnect(void *data, calldata_t *params)
@@ -395,7 +425,7 @@ void OBSBasicStatusBar::ReconnectClear()
 {
 	retries = 0;
 	reconnectTimeout = 0;
-	bitrateUpdateSeconds = -1;
+	seconds = -1;
 	lastBytesSent = 0;
 	lastBytesSentTime = os_gettime_ns();
 	delaySecTotal = 0;
@@ -414,6 +444,8 @@ void OBSBasicStatusBar::ReconnectSuccess()
 	if (streamOutput) {
 		delaySecTotal = obs_output_get_active_delay(streamOutput);
 		UpdateDelayMsg();
+		disconnected = false;
+		firstCongestionUpdate = true;
 	}
 }
 
@@ -518,11 +550,12 @@ void OBSBasicStatusBar::RecordingStopped()
 
 void OBSBasicStatusBar::RecordingPaused()
 {
-	QString text = recordTime->text() + QStringLiteral(" (PAUSED)");
-	recordTime->setText(text);
+	QString text = statusWidget->ui->recordTime->text() +
+		       QStringLiteral(" (PAUSED)");
+	statusWidget->ui->recordTime->setText(text);
 
 	if (recordOutput) {
-		recordIcon->setPixmap(recordingPausePixmap);
+		statusWidget->ui->recordIcon->setPixmap(recordingPausePixmap);
 		streamPauseIconToggle = true;
 	}
 }
@@ -530,6 +563,53 @@ void OBSBasicStatusBar::RecordingPaused()
 void OBSBasicStatusBar::RecordingUnpaused()
 {
 	if (recordOutput) {
-		recordIcon->setPixmap(recordingActivePixmap);
+		statusWidget->ui->recordIcon->setPixmap(recordingActivePixmap);
+	}
+}
+
+static QPixmap GetPixmap(const QString &filename)
+{
+	bool darkTheme = obs_frontend_is_theme_dark();
+	QString path;
+
+	if (darkTheme) {
+		std::string darkPath;
+		QString themePath = QString("themes/Dark/") + filename;
+		GetDataFilePath(QT_TO_UTF8(themePath), darkPath);
+		path = QT_UTF8(darkPath.c_str());
+	} else {
+		path = QString(":/res/images/" + filename);
+	}
+
+	return QIcon(path).pixmap(QSize(16, 16));
+}
+
+void OBSBasicStatusBar::UpdateIcons()
+{
+	disconnectedPixmap = GetPixmap("network-disconnected.svg");
+	inactivePixmap = GetPixmap("network-inactive.svg");
+
+	streamingInactivePixmap = GetPixmap("streaming-inactive.svg");
+
+	recordingInactivePixmap = GetPixmap("recording-inactive.svg");
+	recordingPauseInactivePixmap =
+		GetPixmap("recording-pause-inactive.svg");
+
+	bool streaming = obs_frontend_streaming_active();
+
+	if (!streaming) {
+		statusWidget->ui->streamIcon->setPixmap(
+			streamingInactivePixmap);
+		statusWidget->ui->statusIcon->setPixmap(inactivePixmap);
+	} else {
+		if (disconnected)
+			statusWidget->ui->statusIcon->setPixmap(
+				disconnectedPixmap);
 	}
+
+	bool recording = obs_frontend_recording_active();
+
+	if (!recording)
+		statusWidget->ui->recordIcon->setPixmap(
+			recordingInactivePixmap);
 }

+ 29 - 16
UI/window-basic-status-bar.hpp

@@ -3,30 +3,39 @@
 #include <QStatusBar>
 #include <QPointer>
 #include <QTimer>
-#include <util/platform.h>
 #include <obs.h>
+#include <memory>
 
-class QLabel;
+class Ui_StatusBarWidget;
+
+class StatusBarWidget : public QWidget {
+	Q_OBJECT
+
+	friend class OBSBasicStatusBar;
+
+private:
+	std::unique_ptr<Ui_StatusBarWidget> ui;
+
+public:
+	StatusBarWidget(QWidget *parent = nullptr);
+	~StatusBarWidget();
+};
 
 class OBSBasicStatusBar : public QStatusBar {
 	Q_OBJECT
 
 private:
-	QLabel *delayInfo;
-	QLabel *droppedFrames;
-	QLabel *streamIcon;
-	QLabel *streamTime;
-	QLabel *recordTime;
-	QLabel *recordIcon;
-	QLabel *cpuUsage;
-	QLabel *kbps;
-	QLabel *statusSquare;
+	StatusBarWidget *statusWidget = nullptr;
 
 	obs_output_t *streamOutput = nullptr;
 	obs_output_t *recordOutput = nullptr;
 	bool active = false;
 	bool overloadedNotify = true;
 	bool streamPauseIconToggle = false;
+	bool disconnected = false;
+	bool firstCongestionUpdate = false;
+
+	std::vector<float> congestionArray;
 
 	int retries = 0;
 	int totalStreamSeconds = 0;
@@ -42,14 +51,16 @@ private:
 	int startTotalFrameCount = 0;
 	int lastSkippedFrameCount = 0;
 
-	int bitrateUpdateSeconds = 0;
+	int seconds = 0;
 	uint64_t lastBytesSent = 0;
 	uint64_t lastBytesSentTime = 0;
 
-	QPixmap transparentPixmap;
-	QPixmap greenPixmap;
-	QPixmap grayPixmap;
-	QPixmap redPixmap;
+	QPixmap excellentPixmap;
+	QPixmap goodPixmap;
+	QPixmap mediocrePixmap;
+	QPixmap badPixmap;
+	QPixmap disconnectedPixmap;
+	QPixmap inactivePixmap;
 
 	QPixmap recordingActivePixmap;
 	QPixmap recordingPausePixmap;
@@ -81,6 +92,8 @@ private slots:
 	void ReconnectSuccess();
 	void UpdateStatusBar();
 	void UpdateCPUUsage();
+	void UpdateCurrentFPS();
+	void UpdateIcons();
 
 public:
 	OBSBasicStatusBar(QWidget *parent);