diff --git a/Source/Core/DolphinQt2/Config/LogConfigWidget.cpp b/Source/Core/DolphinQt2/Config/LogConfigWidget.cpp
index e2ad410824..d3ecdb7f05 100644
--- a/Source/Core/DolphinQt2/Config/LogConfigWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/LogConfigWidget.cpp
@@ -19,6 +19,8 @@
 LogConfigWidget::LogConfigWidget(QWidget* parent) : QDockWidget(parent)
 {
   setWindowTitle(tr("Log Configuration"));
+  setObjectName(QStringLiteral("logconfig"));
+
   setHidden(!Settings::Instance().IsLogConfigVisible());
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
diff --git a/Source/Core/DolphinQt2/Config/LogWidget.cpp b/Source/Core/DolphinQt2/Config/LogWidget.cpp
index 1529cbccb9..ac7971ab5b 100644
--- a/Source/Core/DolphinQt2/Config/LogWidget.cpp
+++ b/Source/Core/DolphinQt2/Config/LogWidget.cpp
@@ -29,6 +29,8 @@ constexpr int TIMESTAMP_LENGTH = 10;
 LogWidget::LogWidget(QWidget* parent) : QDockWidget(parent), m_timer(new QTimer(this))
 {
   setWindowTitle(tr("Log"));
+  setObjectName(QStringLiteral("log"));
+
   setHidden(!Settings::Instance().IsLogVisible());
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
diff --git a/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp b/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp
index 6ad3b97477..3e50203778 100644
--- a/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp
+++ b/Source/Core/DolphinQt2/Debugger/BreakpointWidget.cpp
@@ -25,6 +25,8 @@
 BreakpointWidget::BreakpointWidget(QWidget* parent) : QDockWidget(parent)
 {
   setWindowTitle(tr("Breakpoints"));
+  setObjectName(QStringLiteral("breakpoints"));
+
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
   auto& settings = Settings::GetQSettings();
diff --git a/Source/Core/DolphinQt2/Debugger/CodeWidget.cpp b/Source/Core/DolphinQt2/Debugger/CodeWidget.cpp
index 4cbaa1713a..19dff6448b 100644
--- a/Source/Core/DolphinQt2/Debugger/CodeWidget.cpp
+++ b/Source/Core/DolphinQt2/Debugger/CodeWidget.cpp
@@ -27,6 +27,8 @@
 CodeWidget::CodeWidget(QWidget* parent) : QDockWidget(parent)
 {
   setWindowTitle(tr("Code"));
+  setObjectName(QStringLiteral("code"));
+
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
   auto& settings = Settings::GetQSettings();
diff --git a/Source/Core/DolphinQt2/Debugger/MemoryWidget.cpp b/Source/Core/DolphinQt2/Debugger/MemoryWidget.cpp
index 39e64287e9..a0a675a535 100644
--- a/Source/Core/DolphinQt2/Debugger/MemoryWidget.cpp
+++ b/Source/Core/DolphinQt2/Debugger/MemoryWidget.cpp
@@ -32,6 +32,8 @@
 MemoryWidget::MemoryWidget(QWidget* parent) : QDockWidget(parent)
 {
   setWindowTitle(tr("Memory"));
+  setObjectName(QStringLiteral("memory"));
+
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
   CreateWidgets();
diff --git a/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp b/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp
index d95d1a2938..9db31e3690 100644
--- a/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp
+++ b/Source/Core/DolphinQt2/Debugger/RegisterWidget.cpp
@@ -18,6 +18,7 @@
 RegisterWidget::RegisterWidget(QWidget* parent) : QDockWidget(parent)
 {
   setWindowTitle(tr("Registers"));
+  setObjectName(QStringLiteral("registers"));
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
   auto& settings = Settings::GetQSettings();
diff --git a/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp b/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp
index 2a4cc9b8e3..d92c0732f1 100644
--- a/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp
+++ b/Source/Core/DolphinQt2/Debugger/WatchWidget.cpp
@@ -26,6 +26,8 @@ WatchWidget::WatchWidget(QWidget* parent) : QDockWidget(parent)
   // i18n: This kind of "watch" is used for watching emulated memory.
   // It's not related to timekeeping devices.
   setWindowTitle(tr("Watch"));
+  setObjectName(QStringLiteral("watch"));
+
   setAllowedAreas(Qt::AllDockWidgetAreas);
 
   auto& settings = Settings::GetQSettings();
diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp
index e5f9806af9..b5d1b363a9 100644
--- a/Source/Core/DolphinQt2/MainWindow.cpp
+++ b/Source/Core/DolphinQt2/MainWindow.cpp
@@ -108,6 +108,10 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters) : QMainW
 
   if (boot_parameters)
     StartGame(std::move(boot_parameters));
+
+  QSettings& settings = Settings::GetQSettings();
+
+  restoreState(settings.value(QStringLiteral("mainwindow/state")).toByteArray());
 }
 
 MainWindow::~MainWindow()
@@ -116,6 +120,10 @@ MainWindow::~MainWindow()
   ShutdownControllers();
 
   Config::Save();
+
+  QSettings& settings = Settings::GetQSettings();
+
+  settings.setValue(QStringLiteral("mainwindow/state"), saveState());
 }
 
 void MainWindow::InitControllers()
diff --git a/Source/Core/DolphinQt2/MenuBar.cpp b/Source/Core/DolphinQt2/MenuBar.cpp
index c63e3cf472..4eeb5384bf 100644
--- a/Source/Core/DolphinQt2/MenuBar.cpp
+++ b/Source/Core/DolphinQt2/MenuBar.cpp
@@ -338,9 +338,23 @@ void MenuBar::AddViewMenu()
   connect(show_log_config, &QAction::toggled, &Settings::Instance(),
           &Settings::SetLogConfigVisible);
 
+  QAction* show_toolbar = view_menu->addAction(tr("Show &Toolbar"));
+  show_toolbar->setCheckable(true);
+  show_toolbar->setChecked(Settings::Instance().IsToolBarVisible());
+
+  connect(show_toolbar, &QAction::toggled, &Settings::Instance(), &Settings::SetToolBarVisible);
+
   connect(&Settings::Instance(), &Settings::LogVisibilityChanged, show_log, &QAction::setChecked);
   connect(&Settings::Instance(), &Settings::LogConfigVisibilityChanged, show_log_config,
           &QAction::setChecked);
+  connect(&Settings::Instance(), &Settings::ToolBarVisibilityChanged, show_toolbar,
+          &QAction::setChecked);
+
+  QAction* lock_widgets = view_menu->addAction(tr("&Lock Widgets In Place"));
+  lock_widgets->setCheckable(true);
+  lock_widgets->setChecked(Settings::Instance().AreWidgetsLocked());
+
+  connect(lock_widgets, &QAction::toggled, &Settings::Instance(), &Settings::SetWidgetsLocked);
 
   view_menu->addSeparator();
 
diff --git a/Source/Core/DolphinQt2/Settings.cpp b/Source/Core/DolphinQt2/Settings.cpp
index 3c3f1a1fed..8a523605de 100644
--- a/Source/Core/DolphinQt2/Settings.cpp
+++ b/Source/Core/DolphinQt2/Settings.cpp
@@ -366,3 +366,33 @@ bool Settings::IsAnalyticsEnabled() const
 {
   return SConfig::GetInstance().m_analytics_enabled;
 }
+
+void Settings::SetToolBarVisible(bool visible)
+{
+  if (IsToolBarVisible() == visible)
+    return;
+
+  GetQSettings().setValue(QStringLiteral("toolbar/visible"), visible);
+
+  emit ToolBarVisibilityChanged(visible);
+}
+
+bool Settings::IsToolBarVisible() const
+{
+  return GetQSettings().value(QStringLiteral("toolbar/visible")).toBool();
+}
+
+void Settings::SetWidgetsLocked(bool locked)
+{
+  if (AreWidgetsLocked() == locked)
+    return;
+
+  GetQSettings().setValue(QStringLiteral("widgets/locked"), locked);
+
+  emit WidgetLockChanged(locked);
+}
+
+bool Settings::AreWidgetsLocked() const
+{
+  return GetQSettings().value(QStringLiteral("widgets/locked"), true).toBool();
+}
diff --git a/Source/Core/DolphinQt2/Settings.h b/Source/Core/DolphinQt2/Settings.h
index 0f6f3a97e4..dcde3434d6 100644
--- a/Source/Core/DolphinQt2/Settings.h
+++ b/Source/Core/DolphinQt2/Settings.h
@@ -52,6 +52,10 @@ public:
   void SetLogConfigVisible(bool visible);
   bool IsControllerStateNeeded() const;
   void SetControllerStateNeeded(bool needed);
+  void SetToolBarVisible(bool visible);
+  bool IsToolBarVisible() const;
+  void SetWidgetsLocked(bool visible);
+  bool AreWidgetsLocked() const;
 
   // GameList
   QStringList GetPaths() const;
@@ -125,6 +129,8 @@ signals:
   void RegistersVisibilityChanged(bool visible);
   void LogVisibilityChanged(bool visible);
   void LogConfigVisibilityChanged(bool visible);
+  void ToolBarVisibilityChanged(bool visible);
+  void WidgetLockChanged(bool locked);
   void EnableCheatsChanged(bool enabled);
   void WatchVisibilityChanged(bool visible);
   void BreakpointsVisibilityChanged(bool visible);
diff --git a/Source/Core/DolphinQt2/ToolBar.cpp b/Source/Core/DolphinQt2/ToolBar.cpp
index a46efe26b3..521a3a8ea6 100644
--- a/Source/Core/DolphinQt2/ToolBar.cpp
+++ b/Source/Core/DolphinQt2/ToolBar.cpp
@@ -18,10 +18,13 @@ static QSize ICON_SIZE(32, 32);
 ToolBar::ToolBar(QWidget* parent) : QToolBar(parent)
 {
   setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
-  setMovable(false);
+  setMovable(!Settings::Instance().AreWidgetsLocked());
   setFloatable(false);
   setIconSize(ICON_SIZE);
 
+  setWindowTitle(tr("Toolbar"));
+  setObjectName(QStringLiteral("toolbar"));
+
   MakeActions();
   connect(&Settings::Instance(), &Settings::ThemeChanged, this, &ToolBar::UpdateIcons);
   UpdateIcons();
@@ -31,6 +34,11 @@ ToolBar::ToolBar(QWidget* parent) : QToolBar(parent)
 
   connect(&Settings::Instance(), &Settings::DebugModeToggled, this, &ToolBar::OnDebugModeToggled);
 
+  connect(&Settings::Instance(), &Settings::ToolBarVisibilityChanged, this, &ToolBar::setVisible);
+
+  connect(&Settings::Instance(), &Settings::WidgetLockChanged, this,
+          [this](bool locked) { setMovable(!locked); });
+
   OnEmulationStateChanged(Core::GetState());
   OnDebugModeToggled(Settings::Instance().IsDebugModeEnabled());
 }
@@ -49,6 +57,11 @@ void ToolBar::OnEmulationStateChanged(Core::State state)
   m_pause_action->setVisible(playing);
 }
 
+void ToolBar::closeEvent(QCloseEvent*)
+{
+  Settings::Instance().SetToolBarVisible(false);
+}
+
 void ToolBar::OnDebugModeToggled(bool enabled)
 {
   m_step_action->setVisible(enabled);
diff --git a/Source/Core/DolphinQt2/ToolBar.h b/Source/Core/DolphinQt2/ToolBar.h
index 3b8301acdb..aebb422f5e 100644
--- a/Source/Core/DolphinQt2/ToolBar.h
+++ b/Source/Core/DolphinQt2/ToolBar.h
@@ -20,6 +20,7 @@ class ToolBar final : public QToolBar
 public:
   explicit ToolBar(QWidget* parent = nullptr);
 
+  void closeEvent(QCloseEvent*) override;
 signals:
   void OpenPressed();
   void PlayPressed();