1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-10-16 14:10:45 +00:00

Squashed commit of the following:

commit 2bbda3c4e4969fdf05566908fde01f1c9e4e23f7
Author: Stanley <hawkeyesk@outlook.com>
Date:   Mon Sep 5 23:54:39 2022 -0400

    Added in-game panel support

commit ec29b0ec2612b10e45ab9a73c10b88a98fcf6eaf
Author: Stanley <hawkeyesk@outlook.com>
Date:   Sun Sep 4 21:21:37 2022 -0400

    Added in-game panel support

commit 97edc184f349e1fde74a15a643fb90fb9bd90395
Author: Stanley <hawkeyesk@outlook.com>
Date:   Thu Sep 1 18:08:44 2022 -0400

    Update touch panel

commit da48ca0a272290466952c5c1bd1ca035d56f930c
Author: Stanley <hawkeyesk@outlook.com>
Date:   Mon Aug 29 22:19:38 2022 -0400

    Added pop out panel temporary display

commit 701346193804f93616b0e6e2222d9d55223f516f
Author: Stanley <hawkeyesk@outlook.com>
Date:   Wed Aug 24 10:33:59 2022 -0400

    Added auto resize window display mode

commit 98cbcd949f1555b44db22267ce5c54858bef47cd
Author: Stanley <hawkeyesk@outlook.com>
Date:   Wed Aug 24 09:39:38 2022 -0400

    Added auto resize window display mode
This commit is contained in:
Stanley 2022-09-06 00:07:03 -04:00
parent 9555185274
commit 43caff1ca9
52 changed files with 633 additions and 885 deletions

View file

@ -1,3 +0,0 @@
_Temp/
Packages/
PackagesMetadata/

View file

@ -1,30 +0,0 @@
<AssetPackage Name="pop-out-manager" Version="1.0.0">
<ItemSettings>
<ContentType>SPB</ContentType>
<Title>pop-out-manager</Title>
<Manufacturer>Stanley Kwok</Manufacturer>
<Creator>Stanley Kwok</Creator>
</ItemSettings>
<Flags>
<VisibleInStore>false</VisibleInStore>
<CanBeReferenced>false</CanBeReferenced>
</Flags>
<AssetGroups>
<AssetGroup Name="pop-out-manager">
<Type>SPB</Type>
<Flags>
<FSXCompatibility>false</FSXCompatibility>
</Flags>
<AssetDir>PackageSources\pop-out-manager\</AssetDir>
<OutputDir>InGamePanels\</OutputDir>
</AssetGroup>
<AssetGroup Name="html-ui">
<Type>Copy</Type>
<Flags>
<FSXCompatibility>false</FSXCompatibility>
</Flags>
<AssetDir>PackageSources\html_ui\</AssetDir>
<OutputDir>html_ui\</OutputDir>
</AssetGroup>
</AssetGroups>
</AssetPackage>

View file

@ -1,138 +0,0 @@
body.modal-open {
pointer-events: none;
}
body.modal-open .stepDialog {
pointer-events: auto;
}
pop-out-manager #popOutManager .ingameUiWrapper .horizontalLayout {
background-color: var(--backgroundColorPanel);
display: flex;
flex-direction: column;
padding: 1em 1em;
}
pop-out-manager #popOutManager .ingameUiWrapper .horizontalLayout #profileInfo {
flex: 1 1 0;
margin-bottom: 1em;
}
pop-out-manager #popOutManager .ingameUiWrapper .horizontalLayout #panelConfigTable {
display: flex;
flex-direction: column;
border: solid 1px white;
flex: 20 1 0;
}
pop-out-manager #popOutManager .ingameUiWrapper .horizontalLayout #panelConfigButtons {
display: flex;
flex-direction: row;
position: relative;
margin: 2em 1em 1em 1em;
flex: 2 1 0;
}
pop-out-manager #popOutManager .ingameUiWrapper .panelConfigButton {
display: flex;
justify-content: center;
width: 6em;
height: 3em;
margin-right: 1em;
}
pop-out-manager #popOutManager .ingameUiWrapper .lockPanelsButton {
display: flex;
justify-content: center;
position: absolute;
right: 0;
width: 10em;
height: 3em;
}
.panelRow {
display: flex;
margin: 0;
padding: 0;
}
.panelRow .column1 {
width: 34%;
}
.panelRow .column2 {
width: 8%;
}
.panelRow .column3 {
width: 8%;
}
.panelRow .column4 {
width: 8%;
}
.panelRow .column5 {
width: 8%;
}
.panelRow .column6 {
width: 8%;
}
.panelRow .column7 {
width: 8%;
}
.panelRow .column8 {
width: 10%;
}
.panelRow .column9 {
width: 8%;
}
.panelCell {
display: flex;
padding: 0.7em 1em;
margin: 0;
justify-content: center;
text-align: center;
border-bottom: solid 1px white;
border-right: solid 1px white;
}
.panelCell:last-child {
border-right: none;
}
.panelCell .alignCenter {
display: flex;
justify-content: center;
align-items: center;
}
.panelCell ui-input .default-input input {
padding-left: 0.25em;
padding-right: 0.25em;
}
.panelCell checkbox-element .checkbox .choices .iconWrapper .icon {
width: 1.6em;
height: 1.6em;
}
.stepDialog {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
left: 20vw;
top: 20vh;
width: 60vw; /* Full width */
height: 60vh; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgba(161, 153, 153, 1);
}

View file

@ -1,74 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="/SCSS/common.css" />
<link rel="stylesheet" href="PopOutManager.css" />
<script type="text/javascript" src="/JS/coherent.js"></script>
<script type="text/javascript" src="/JS/common.js"></script>
<script type="text/javascript" src="/JS/buttons.js"></script>
<script type="text/javascript" src="/JS/Services/ToolBarPanels.js"></script>
<script type="text/javascript" src="/JS/Services/Notifications.js"></script>
<link rel="import" href="/templates/virtualScroll/virtualScroll.html" />
<link rel="import" href="/templates/NewPushButton/NewPushButton.html" />
<link rel="import" href="/templates/DropDown/DropDown.html" />
<link rel="import" href="/templates/tabMenu/tabMenu.html" />
<link rel="import" href="/templates/ingameUi/ingameUi.html" />
<link rel="import" href="/templates/ingameUiHeader/ingameUiHeader.html" />
<link rel="import" href="/templates/checkbox/checkbox.html" />
<link rel="import" href="/templates/uiInput/uiInput.html" />
<script type="text/javascript" src="PopOutManager.js"></script>
</head>
<body class="border-box">
<pop-out-manager>
<ingame-ui id="popOutManager" panel-id="PANEL_POP_OUT_MANAGER" title=""
class="ingameUiFrame panelInvisible condensedPanel" resize="both" min-width="160" min-height="90"
content-fit="true" auto-inside>
<div id="panelSelection" class="horizontalLayout" style="display:block">
<div id="profileInfo">Panel Locations and Settings - <span id="planeProfileName"></span></div>
<div id="panelSelection">
Panel Selection
<drop-down id="dropdownProfile" style="width:50vw"></drop-down>
<icon-button id="addProfile" data-url="/icons/open.svg"></icon-button>
<icon-button id="deleteProfile" data-url="/icons/reduce.svg"></icon-button>
</div>
</div>
<div id="panelConfiguration" class="horizontalLayout" style="display:none">
<div id="profileInfo">Panel Locations and Settings - <span id="planeProfileName"></span></div>
<virtual-scroll id="panelConfigTable" direction="y">
</virtual-scroll>
<div id="panelConfigButtons">
<new-push-button id="btnMinusTen" title="-10 px" class="panelConfigButton"></new-push-button>
<new-push-button id="btnMinusOne" title="-1 px" class="panelConfigButton"></new-push-button>
<new-push-button id="btnPlusOne" title="+1 px" class="panelConfigButton"></new-push-button>
<new-push-button id="btnPlusTen" title="+10 px" class="panelConfigButton" toolTip="test"></new-push-button>
<div>
<new-push-button id="btnLockPanels" title="Lock Panels" class="lockPanelsButton"></new-push-button>
</div>
</div>
</div>
</ingame-ui>
</pop-out-manager>
<div id="dialogBegin" class="stepDialog">
<new-push-button id="btnStartPopOut" parentStep="stepBegin" title="Start Pop Out" class="stepButton"></new-push-button>
<new-push-button id="btnCreateNewProfile" parentStep="stepBegin" title="Create New Profile" class="stepButton"></new-push-button>
<new-push-button id="btnAdjustProfile" parentStep="stepBegin" title="Adjust Profile" class="stepButton"></new-push-button>
</div>
</body>
</html>

View file

@ -1,388 +0,0 @@
class PopOutManagerPanelElement extends UIElement {
constructor() {
super(...arguments);
this.ingameUi = null;
this.isInitialized = false;
this.panelActive = false;
this.webSocket = null;
this.webSocketConnected = false;
this.webSocketInterval = null;
this.tryConnectWebSocket = () => {
this.lockPanel(true);
this.webSocket = new WebSocket("ws://localhost:27011/ws");
this.webSocket.onopen = () => {
clearInterval(this.webSocketInterval);
this.webSocketConnected = true;
};
this.webSocket.onclose = () => {
this.webSocketConnected = false;
// Clear panel table
this.createPanelConfigTableHeader(this.panelConfigTable);
this.lockPanel(true);
this.webSocketInterval = setInterval(() => {
if (!this.webSocketConnected)
this.tryConnectWebSocket();
}, 2000)
};
this.webSocket.onerror = () => {
this.webSocket.close();
};
this.webSocket.onmessage = (event) => {
if (event.data !== undefined) {
var panelData = JSON.parse(event.data);
// only recreate panel rows if panel is refreshed (minimize/maximize, pop out)
if (this.panelActive && document.getElementsByClassName("panelRow").length == 1)
this.createPanelConfigTableBody(this.panelConfigTable, panelData);
if (panelData !== undefined && panelData !== null && panelData.length !== 0) {
this.lockPanel(false);
this.bindPanelData(panelData);
}
}
};
}
this.webSocketInterval = setInterval(() => {
if (!this.webSocketConnected)
this.tryConnectWebSocket();
}, 2000)
}
connectedCallback() {
super.connectedCallback();
this.ingameUi = this.querySelector('ingame-ui');
this.panelSelection = document.getElementById("panelSelection");
this.panelConfiguration = document.getElementById("panelConfiguration");
this.planeProfileName = document.getElementById("planeProfileName");
this.panelConfigTable = document.getElementById("panelConfigTable");
this.btnLockPanels = this.querySelector('#btnLockPanels');
this.btnPlusTen = this.querySelector('#btnPlusTen');
this.btnPlusOne = this.querySelector('#btnPlusOne');
this.btnMinusTen = this.querySelector('#btnMinusTen');
this.btnMinusOne = this.querySelector('#btnMinusOne');
this.dropdownProfile = this.querySelector("#dropdownProfile");
this.addProfile = document.getElementById("addProfile");
this.deleteProfile = document.getElementById("deleteProfile");
this.stepBeginDialog = document.getElementById("stepBeginDialog");
this.closeDialog = document.getElementById("closeDialog");
//this.deleteProfile.disable(!this.deleteProfile.disabled);
//this.dropdownProfile.addEventListener("select", (event) = {});
let workflow = new Workflow(this);
workflow.bindAllButtonEvents();
workflow.stepBegin();
this.addProfile.addEventListener("click", (event) => {
addProfileDialog.style.display = "block";
//this.ingameUi.toggleExternPanel(true);
document.body.classList.toggle('modal-open');
// let value = new DataValue;
// value.name = "New Profile " + dropDownValues.length;
// value.ID = dropDownValues.length;
// dropDownValues.push(value);
// this.dropdownProfile.setData(dropDownValues, dropDownValues.length - 1);
});
this.deleteProfile.addEventListener("click", (event) => {
// dropDownValues.splice(-1,1);
// this.dropdownProfile.setData(dropDownValues, dropDownValues.length - 1);
});
this.isLocked = false;
let profiles = ["Kodiak", "172", "737"];
let dropDownValues = [];
profiles.forEach((profile, index) => {
let value = new DataValue;
value.name = profile;
value.ID = index;
dropDownValues.push(value);
});
setTimeout(() => {
this.dropdownProfile.setData(dropDownValues, 2)
}, 1000);
this.btnLockPanels.addEventListener("click", () => {
if (this.btnLockPanels.disabled)
return;
this.isLocked = !this.isLocked;
this.lockPanel(this.isLocked);
if (this.isLocked) {
this.btnLockPanels.disabled = false;
this.btnLockPanels.title = "Unlock Panels";
this.btnLockPanels.style.backgroundColor = "red";
}
else {
this.btnLockPanels.disabled = false;
this.btnLockPanels.title = "Lock Panels";
this.btnLockPanels.style.backgroundColor = null;
}
this.panelSelection.style.display = 'block';
this.panelConfiguration.style.display = 'none';
});
if (this.ingameUi) {
this.ingameUi.addEventListener("panelActive", (e) => {
//document.getElementsByClassName("Extern")[0].style.display = "none"; // disable extern button
this.createPanelConfigTableHeader(this.panelConfigTable);
this.panelActive = true;
this.lockPanel(true);
if (this.webSocketConnected)
this.webSocket.send("RequestPanelData");
});
}
}
createPanelConfigTableHeader(panelConfigTable) {
let panelRow;
// remove all child of panelConfigTable
while (panelConfigTable.firstChild) {
panelConfigTable.removeChild(panelConfigTable.firstChild);
}
// create header
panelRow = this.createPanelRow(true);
panelRow.appendChild(this.createPanelCell("div", "column1", "Panel Name"));
panelRow.appendChild(this.createPanelCell("div", "column2", "X-Pos"));
panelRow.appendChild(this.createPanelCell("div", "column3", "Y-Pos"));
panelRow.appendChild(this.createPanelCell("div", "column4", "Width"));
panelRow.appendChild(this.createPanelCell("div", "column5", "Height"));
panelRow.appendChild(this.createPanelCell("div", "column6", "Always on Top"));
panelRow.appendChild(this.createPanelCell("div", "column7", "Hide Title Bar"));
panelRow.appendChild(this.createPanelCell("div", "column8", "Full Screen Mode"));
panelRow.appendChild(this.createPanelCell("div", "column9", "Touch Enabled"));
panelConfigTable.appendChild(panelRow);
}
createPanelConfigTableBody(panelConfigTable, panelData) {
let panelRow;
if (panelConfigTable !== undefined) {
for (let index = 0; index < panelData.length; index++) {
panelRow = this.createPanelRow(false);
panelRow.appendChild(this.createPanelCell("div", "column1", this.createUiInput("PanelName_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column2", this.createUiInput("XPos_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column3", this.createUiInput("YPos_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column4", this.createUiInput("Width_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column5", this.createUiInput("Height_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column6", this.createCheckbox("AlwaysOnTop_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column7", this.createCheckbox("HideTitleBar_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column8", this.createCheckbox("FullScreenMode_" + index)));
panelRow.appendChild(this.createPanelCell("div", "column9", this.createCheckbox("TouchEnabled_" + index)));
panelConfigTable.appendChild(panelRow);
}
}
}
createPanelRow(isHeaderRow) {
let panelRow = document.createElement("div");
if (isHeaderRow)
panelRow.classList.add("panelHeaderRow");
panelRow.classList.add("panelRow");
return panelRow;
}
createPanelCell(cellType, classes = null, childElement) {
let cell = document.createElement(cellType);
cell.classList.add("panelCell");
if (classes !== undefined || classes !== null) {
if (typeof (classes) === "string") {
cell.classList.add(classes);
}
else {
for (let index = 0; index < classes.length; index++)
cell.classList.add(classes[index]);
}
}
if (typeof (childElement) === "string")
cell.innerHTML = `<div class='alignCenter'>${childElement}</div>`;
else
cell.appendChild(childElement);
return cell;
}
createUiInput(id) {
let input = document.createElement("ui-input");
input.type = "text";
input.id = id;
return input;
}
createCheckbox(id) {
let checkbox = document.createElement("checkbox-element");
checkbox.id = id;
checkbox.style.width = "1em";
return checkbox;
}
lockPanel(isLocked) {
if (this.panelActive) {
let uiInputs = document.getElementsByTagName("ui-input");
for (let index = 0; index < uiInputs.length; index++) {
uiInputs[index].disabled = isLocked;
}
let checkboxes = document.getElementsByTagName("checkbox-element");
for (let index = 0; index < checkboxes.length; index++) {
checkboxes[index].SetData({ sTitle: "", bToggled: checkboxes.toggled, bDisabled: isLocked });
checkboxes[index].RefreshValue();
}
this.btnLockPanels.disabled = isLocked;
if(isLocked)
{
this.btnLockPanels.title = "Lock Panels";
this.btnLockPanels.style.backgroundColor = null;
}
this.btnPlusTen.disabled = isLocked;
this.btnPlusOne.disabled = isLocked;
this.btnMinusTen.disabled = isLocked;
this.btnMinusOne.disabled = isLocked;
}
}
bindPanelData(panelData) {
if (this.panelActive) {
panelData.forEach((panel, index) => {
this.bindUiInput("PanelName_" + index, panel.panelName, "text");
this.bindUiInput("XPos_" + index, panel.xPos);
this.bindUiInput("YPos_" + index, panel.yPos);
this.bindUiInput("Width_" + index, panel.width);
this.bindUiInput("Height_" + index, panel.height);
this.bindCheckbox("AlwaysOnTop_" + index, panel.alwaysOnTop);
this.bindCheckbox("HideTitleBar_" + index, panel.hideTitleBar);
this.bindCheckbox("FullScreenMode_" + index, panel.fullScreenMode);
this.bindCheckbox("TouchEnabled_" + index, panel.touchEnabled);
})
}
}
bindUiInput(id, value, type) {
let input = document.getElementById(id);
input.style.width = "100%";
input.setValue(value);
if (type === "text")
input.children[0].children[0].style.textAlign = "left";
}
bindCheckbox(id, value) {
let checkbox = document.getElementById(id);
checkbox.SetData({ sTitle: "", bToggled: value, bDisabled: false });
checkbox.RefreshValue();
}
}
window.customElements.define("pop-out-manager", PopOutManagerPanelElement);
class Workflow {
constructor(owner) {
this.owner = owner;
}
get workflowSteps() {
return [
{
name: 'stepBegin',
results:
[
{ value: 'stepCreateNewProfile' },
{ value: 'stepStartPopOut' },
{ value: 'stepAdjustProfile' }
]
}]
}
getFuncName() {
return this.getFuncName.caller.name;
}
bindAllButtonEvents(){
let stepButtons = document.getElementsByClassName("stepButton");
Array.from(stepButtons).forEach(btn => {
btn.addEventListener("click", (e) => {
let parentStep = e.target.getAttribute("parentStep");
let resultValue = e.target.id.replace("btn", "step");
this.handleButtonClick(parentStep, resultValue);
})
})
}
handleButtonClick(currentStep, resultValue) {
let step = this.workflowSteps.find(c => c.name == currentStep);
let result = step.results.find(f => f.value == resultValue);
if(result != null)
{
let func = this[result.value];
func();
}
}
openDialog(step)
{
this.owner.stepBeginDialog = document.getElementById(step.replace("step", "dialog"));
this.owner.stepBeginDialog.style.display = "block";
document.body.classList.toggle("modal-open");
}
stepBegin() {
this.openDialog("stepBegin");
}
stepStartPopOut() {
var a = ""
}
stepCreateNewProfile() {
var a = ""
}
stepAdjustProfile() {
var a = ""
}
}

View file

@ -1,16 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="64.000000pt" height="64.000000pt" viewBox="0 0 64.000000 64.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,64.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M160 315 l0 -165 160 0 160 0 0 50 c0 43 -3 50 -20 50 -16 0 -20 -7
-20 -30 l0 -30 -120 0 -120 0 0 125 0 125 30 0 c23 0 30 4 30 20 0 17 -7 20
-50 20 l-50 0 0 -165z"/>
<path d="M358 447 l32 -33 -50 -49 c-47 -46 -49 -50 -33 -67 16 -18 19 -17 68
32 l51 50 27 -27 27 -28 0 78 0 77 -77 0 -77 0 32 -33z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 740 B

View file

@ -1,29 +0,0 @@
{
"content": [
{
"path": "html_ui/icons/toolbar/POP_OUT_MANAGER.svg",
"size": 740,
"date": 133013771715368552
},
{
"path": "html_ui/InGamePanels/PopOutManagerPanel/PopOutManager.css",
"size": 1921,
"date": 133015501657300878
},
{
"path": "html_ui/InGamePanels/PopOutManagerPanel/PopOutManager.html",
"size": 3194,
"date": 133023923647110813
},
{
"path": "html_ui/InGamePanels/PopOutManagerPanel/PopOutManager.js",
"size": 10854,
"date": 133023924937932717
},
{
"path": "InGamePanels/pop-out-manager.spb",
"size": 684,
"date": 133023927549810418
}
]
}

View file

@ -1,15 +0,0 @@
{
"dependencies": [],
"content_type": "UNKNOWN",
"title": "pop-out-manager",
"manufacturer": "Stanley Kwok",
"creator": "Stanley Kwok",
"package_version": "1.0.0",
"minimum_game_version": "1.27.9",
"release_notes": {
"neutral": {
"LastUpdate": "",
"OlderHistory": ""
}
}
}

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<SimBase.Document Type="InGamePanels" version="1,0">
<Filename>InGamePanel_PopOutManager.spb</Filename>
<InGamePanels.InGamePanelDefinition id="PANEL_POP_OUT_MANAGER" Name="Pop Out Manager" url="html_UI/ingamePanels/PopOutManagerPanel/PopOutManager.html" resizeDirections="Both" minWidth="80" minHeight="40" defaultWidth="120" defaultHeight="50" defaultTop="30" defaultLeft="5" icon="POP_OUT_MANAGER" buttonVisible="true">
</InGamePanels.InGamePanelDefinition>
</SimBase.Document>

View file

@ -1 +0,0 @@
FOR %%i IN (*.xml) DO "%MSFS_SDK%\Tools\bin\fspackagetool.exe" %%i

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View file

@ -1,7 +0,0 @@
<Project Version="2" Name="pop-out-manager" FolderName="Packages">
<OutputDirectory>.</OutputDirectory>
<TemporaryOutputDirectory>_Temp</TemporaryOutputDirectory>
<Packages>
<Package>PackageDefinitions\pop-out-manager.xml</Package>
</Packages>
</Project>

View file

@ -10,9 +10,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.ArduinoAgent</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -9,8 +9,12 @@ namespace MSFSPopoutPanelManager.Orchestration
{
public class MainOrchestrator : ObservableObject
{
private IntPtr _msfsGameWindowHandle;
public MainOrchestrator()
{
_msfsGameWindowHandle = IntPtr.Zero;
Profile = new ProfileOrchestrator();
PanelSource = new PanelSourceOrchestrator();
PanelPopOut = new PanelPopOutOrchestrator();

View file

@ -10,9 +10,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.Orchestration</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -15,6 +15,7 @@ namespace MSFSPopoutPanelManager.Orchestration
{
// This will be replaced by a signal from Ready to Fly Skipper into webserver in version 4.0
private const int READY_TO_FLY_BUTTON_APPEARANCE_DELAY = 2000;
private int _builtInPanelConfigDelay;
internal ProfileData ProfileData { get; set; }
@ -35,6 +36,7 @@ namespace MSFSPopoutPanelManager.Orchestration
public event EventHandler OnPopOutStarted;
public event EventHandler<bool> OnPopOutCompleted;
public event EventHandler<TouchPanelOpenEventArg> OnTouchPanelOpened;
public event EventHandler<PanelSourceCoordinate> OnPanelSourceOverlayFlashed;
public void ManualPopOut()
{
@ -43,9 +45,10 @@ namespace MSFSPopoutPanelManager.Orchestration
InputHookManager.EndHook();
if (ActiveProfile.PanelSourceCoordinates.Count > 0 || ActiveProfile.TouchPanelBindings.Count > 0)
if (ActiveProfile.PanelSourceCoordinates.Count > 0 || ActiveProfile.TouchPanelBindings.Count > 0 || ActiveProfile.IncludeInGamePanels)
{
StatusMessageWriter.WriteMessage($"Panels pop out in progress for profile:\n{ActiveProfile.ProfileName}", StatusMessageType.Info, true);
_builtInPanelConfigDelay = 0;
CorePopOutSteps();
}
}
@ -67,19 +70,24 @@ namespace MSFSPopoutPanelManager.Orchestration
// Match the delay for Ready to Fly button to disappear
Thread.Sleep(READY_TO_FLY_BUTTON_APPEARANCE_DELAY);
if (ActiveProfile.PanelSourceCoordinates.Count > 0 || ActiveProfile.TouchPanelBindings.Count > 0)
if (ActiveProfile.PanelSourceCoordinates.Count > 0 || ActiveProfile.TouchPanelBindings.Count > 0 || ActiveProfile.IncludeInGamePanels)
{
StatusMessageWriter.WriteMessage($"Automatic pop out is starting for profile:\n{profile.ProfileName}", StatusMessageType.Info, true);
// Extra wait for cockpit view to appear and align
Thread.Sleep(2000);
_builtInPanelConfigDelay = 4000;
CorePopOutSteps();
}
}
private void CorePopOutSteps()
{
// Set Windowed Display Mode window's configuration if needed
if (AppSettingData.AppSetting.AutoResizeMsfsGameWindow)
WindowActionManager.SetMsfsGameWindowLocation(ActiveProfile.MsfsGameWindowConfig);
// Has custom pop out panels
if (ActiveProfile.PanelSourceCoordinates.Count > 0)
{
@ -141,7 +149,7 @@ namespace MSFSPopoutPanelManager.Orchestration
return;
}
if (ActiveProfile.PanelSourceCoordinates.Count == 0 && ActiveProfile.TouchPanelBindings.Count == 0)
if (ActiveProfile.PanelSourceCoordinates.Count == 0 && ActiveProfile.TouchPanelBindings.Count == 0 && !ActiveProfile.IncludeInGamePanels)
{
StatusMessageWriter.WriteMessage("No panel has been selected for the profile. Please select at least one panel to continue.", StatusMessageType.Error, false);
return;
@ -179,14 +187,24 @@ namespace MSFSPopoutPanelManager.Orchestration
// Add the MSFS Touch Panel (My other github project) windows to the panel list
if (AppSetting.TouchPanelSettings.EnableTouchPanelIntegration)
{
var panelResults = AddMsfsTouchPanels(panelConfigs.Count + 1);
var panelResults = AddMsfsTouchPanels(panelConfigs.Count + 100); // add a panelIndex gap
if (panelResults != null)
panelConfigs.AddRange(panelResults);
}
// Add the built-in panels from toolbar menu (ie. VFR Map, Check List, Weather, etc)
if (ActiveProfile.IncludeInGamePanels)
{
// Allow delay to wait for in game built-in pop outs to appear
Thread.Sleep(_builtInPanelConfigDelay);
var panelResults = AddBuiltInPanels(panelConfigs.Count + 200); // add a panelIndex gap
if (panelResults != null)
panelConfigs.AddRange(panelResults);
}
if (panelConfigs.Count == 0)
{
OnPopOutCompleted?.Invoke(this, false);
StatusMessageWriter.WriteMessage("No panels have been found. Please select at least one in-game panel.", StatusMessageType.Error, false);
return;
}
@ -217,6 +235,13 @@ namespace MSFSPopoutPanelManager.Orchestration
StatusMessageWriter.WriteMessage("Panels have been popped out succesfully.", StatusMessageType.Info, true);
OnPopOutCompleted?.Invoke(this, true);
}
if (!ActiveProfile.IsLocked)
ProfileData.WriteProfiles();
// For migrating existing profile, if using windows mode, save MSFS game window configuration
if (AppSettingData.AppSetting.AutoResizeMsfsGameWindow && !ActiveProfile.MsfsGameWindowConfig.IsValid)
ProfileData.SaveMsfsGameWindowConfig();
}
}
@ -229,18 +254,25 @@ namespace MSFSPopoutPanelManager.Orchestration
{
var x = ActiveProfile.PanelSourceCoordinates[i - 1].X;
var y = ActiveProfile.PanelSourceCoordinates[i - 1].Y;
// show the panel source overlay for split second
Task task = new Task(() => OnPanelSourceOverlayFlashed?.Invoke(this, ActiveProfile.PanelSourceCoordinates[i - 1]));
task.RunSynchronously();
InputEmulationManager.PopOutPanel(x, y, AppSetting.UseLeftRightControlToPopOut);
var handle = PInvoke.FindWindow("AceApp", String.Empty); // Get an AceApp window with empty title
// Get an AceApp window with empty title
var handle = PInvoke.FindWindow("AceApp", String.Empty);
// Need to move the window to upper left corner first. There is a possible bug in the game that panel pop out to full screen that prevents further clicking.
if (handle != IntPtr.Zero)
WindowActionManager.MoveWindow(handle, 0, 0, 1000, 500);
// The joined panel is always the first panel that got popped out
if (i > 1)
SeparatePanel(panels[0].PanelHandle); // The joined panel is always the first panel that got popped out
SeparatePanel(panels[0].PanelHandle);
handle = PInvoke.FindWindow("AceApp", String.Empty); // Get an AceApp window with empty title
handle = PInvoke.FindWindow("AceApp", String.Empty);
if (handle == IntPtr.Zero && i == 1)
{
@ -293,6 +325,35 @@ namespace MSFSPopoutPanelManager.Orchestration
InputEmulationManager.LeftClick(point.X, point.Y);
}
private List<PanelConfig> AddBuiltInPanels(int panelIndex)
{
List<PanelConfig> builtinPanels = new List<PanelConfig>();
var panelHandles = WindowActionManager.GetWindowsByPanelType(new List<PanelType>() { PanelType.BuiltInPopout });
foreach (var panelHandle in panelHandles)
{
var rectangle = WindowActionManager.GetWindowRect(panelHandle);
var clientRectangle = WindowActionManager.GetClientRect(panelHandle);
builtinPanels.Add(new PanelConfig()
{
PanelIndex = panelIndex,
PanelHandle = panelHandle,
PanelType = PanelType.BuiltInPopout,
PanelName = WindowActionManager.GetWindowCaption(panelHandle),
Top = rectangle.Top,
Left = rectangle.Left,
Width = clientRectangle.Width,
Height = clientRectangle.Height
});
panelIndex++;
}
return builtinPanels.Count == 0 ? null : builtinPanels;
}
private List<PanelConfig> AddMsfsTouchPanels(int panelIndex)
{
List<PanelConfig> touchPanels = new List<PanelConfig>();
@ -357,7 +418,7 @@ namespace MSFSPopoutPanelManager.Orchestration
}
}
return touchPanels;
return touchPanels.Count == 0 ? null : touchPanels;
}
private void LoadAndApplyPanelConfigs(List<PanelConfig> panelResults)
@ -368,7 +429,18 @@ namespace MSFSPopoutPanelManager.Orchestration
if (panel.PanelHandle == IntPtr.Zero)
return;
var savedPanelConfig = ActiveProfile.PanelConfigs.FirstOrDefault(s => s.PanelIndex == panel.PanelIndex);
PanelConfig savedPanelConfig = null;
if (panel.PanelType == PanelType.CustomPopout || panel.PanelType == PanelType.MSFSTouchPanel)
savedPanelConfig = ActiveProfile.PanelConfigs.FirstOrDefault(s => s.PanelIndex == panel.PanelIndex);
else if (panel.PanelType == PanelType.BuiltInPopout)
savedPanelConfig = ActiveProfile.PanelConfigs.FirstOrDefault(s => s.PanelName == panel.PanelName);
if (savedPanelConfig == null)
{
panel.PanelHandle = IntPtr.Zero;
return;
}
// Assign previous saved values
if (savedPanelConfig != null)
@ -401,6 +473,14 @@ namespace MSFSPopoutPanelManager.Orchestration
Thread.Sleep(1000);
}
// Apply window size again to overcome a bug in MSFS that when moving panel between monitors, panel automatic resize for no reason
if (panel.PanelType == PanelType.BuiltInPopout)
{
Thread.Sleep(2000); // Overcome GTN750 bug
WindowActionManager.MoveWindow(panel.PanelHandle, panel.Left, panel.Top, panel.Width, panel.Height);
Thread.Sleep(1000);
}
if (!panel.FullScreen)
{
// Apply always on top
@ -428,6 +508,10 @@ namespace MSFSPopoutPanelManager.Orchestration
Thread.Sleep(250);
}
});
// If profile is locked, remove all panels without handle
if (ActiveProfile.IsLocked)
panelResults.RemoveAll(p => p.PanelHandle == IntPtr.Zero);
}
private void ReturnToAfterPopOutCameraView()

View file

@ -23,7 +23,7 @@ namespace MSFSPopoutPanelManager.Orchestration
private AppSetting AppSetting { get { return AppSettingData == null ? null : AppSettingData.AppSetting; } }
public event EventHandler<PanelSourceCoordinate> onOverlayShowed;
public event EventHandler<PanelSourceCoordinate> OnOverlayShowed;
public event EventHandler OnLastOverlayRemoved;
public event EventHandler OnAllOverlaysRemoved;
public event EventHandler OnSelectionStarted;
@ -71,6 +71,11 @@ namespace MSFSPopoutPanelManager.Orchestration
}
InputEmulationManager.SaveCustomView(AppSettingData.AppSetting.AutoPanningKeyBinding);
// If using windows mode, save MSFS game window configuration
if (AppSettingData.AppSetting.AutoResizeMsfsGameWindow)
ProfileData.SaveMsfsGameWindowConfig();
StatusMessageWriter.WriteMessage("Auto Panning Camera has been saved succesfully.", StatusMessageType.Info, false);
}
@ -100,7 +105,7 @@ namespace MSFSPopoutPanelManager.Orchestration
ActiveProfile.PanelSourceCoordinates.Add(newCoor);
_panelIndex++;
onOverlayShowed?.Invoke(this, newCoor);
OnOverlayShowed?.Invoke(this, newCoor);
}
public void HandleOnLastPanelSelectionRemoved(object sender, Point e)
@ -122,12 +127,16 @@ namespace MSFSPopoutPanelManager.Orchestration
if (ActiveProfile == null)
return;
// Set Windowed Display Mode window's configuration if needed
if (AppSettingData.AppSetting.AutoResizeMsfsGameWindow)
WindowActionManager.SetMsfsGameWindowLocation(ActiveProfile.MsfsGameWindowConfig);
// remove all existing panel overlay display
for (var i = 0; i < ActiveProfile.PanelSourceCoordinates.Count; i++)
OnAllOverlaysRemoved?.Invoke(this, null);
foreach (var coor in ActiveProfile.PanelSourceCoordinates)
onOverlayShowed?.Invoke(this, new PanelSourceCoordinate() { PanelIndex = coor.PanelIndex, X = coor.X, Y = coor.Y });
OnOverlayShowed?.Invoke(this, new PanelSourceCoordinate() { PanelIndex = coor.PanelIndex, X = coor.X, Y = coor.Y });
// Turn off TrackIR if TrackIR is started
FlightSimOrchestrator.TurnOffTrackIR();
@ -141,7 +150,7 @@ namespace MSFSPopoutPanelManager.Orchestration
if (ActiveProfile == null)
return;
// remove all existing panel overlay display
// Remove all existing panel overlay display
for (var i = 0; i < ActiveProfile.PanelSourceCoordinates.Count; i++)
OnAllOverlaysRemoved?.Invoke(this, null);
@ -154,12 +163,16 @@ namespace MSFSPopoutPanelManager.Orchestration
if (ActiveProfile == null)
return;
//If enable, save the current viewport into custom view by Ctrl-Alt-0
// If enable, save the current viewport into custom view by Ctrl-Alt-0
if (AppSetting.UseAutoPanning)
InputEmulationManager.SaveCustomView(AppSetting.AutoPanningKeyBinding);
ProfileData.WriteProfiles();
// If using windows mode, save MSFS game window configuration
if (AppSettingData.AppSetting.AutoResizeMsfsGameWindow)
ProfileData.SaveMsfsGameWindowConfig();
InputHookManager.EndHook();
// Turn TrackIR back on

View file

@ -204,5 +204,18 @@ namespace MSFSPopoutPanelManager.Orchestration
{
MigrateLiveryToAircraftBinding(FlightSimData.CurrentMsfsLiveryTitle, FlightSimData.CurrentMsfsAircraft);
}
public void SaveMsfsGameWindowConfig()
{
if (ActiveProfile == null)
return;
var msfsGameWindowConfig = WindowsAgent.WindowActionManager.GetMsfsGameWindowLocation();
if (msfsGameWindowConfig.IsValid)
{
ActiveProfile.MsfsGameWindowConfig = msfsGameWindowConfig;
WriteProfiles();
}
}
}
}

View file

@ -0,0 +1,155 @@
const SIMCONNECT_DATA_REQUEST_INTERVAL_SLOW = 5000;
var apiUrl = undefined;
var networkStatus = false;
var arduinoStatus = false;
var simConnectSystemEvent = undefined;
var simConnectData = undefined;
onconnect = (ev) => {
const [port] = ev.ports;
port.onmessage = e => {
apiUrl = e.data.apiUrl;
updateInterval = e.data.updateInterval;
setInterval(() => {
port.postMessage({
networkStatus: networkStatus,
arduinoStatus: arduinoStatus,
simConnectSystemEvent: simConnectSystemEvent,
simConnectData: simConnectData
});
}, updateInterval);
requestData(updateInterval);
};
};
requestData = async (updateInterval) => {
try {
let response = await fetch(`${apiUrl}/getdata`).catch(() => {
throw('MSFS Touch Panel Server is unavailable.')
});
if (response !== undefined) {
let result = await response.json();
if (result === undefined)
throw new Error('MSFS Touch Panel Server error');
networkStatus = Boolean(result.msfsStatus ?? false);
arduinoStatus = Boolean(result.arduinoStatus ?? false);
if (result.systemEvent !== null && result.systemEvent !== undefined)
simConnectSystemEvent = result.systemEvent.split('-')[0];
else
simConnectSystemEvent = null;
if (!result.msfsStatus)
throw('MSFS SimConnect is unavailable.')
var simData = JSON.parse(result.data ?? []);
if ((simData !== null && simData !== []))
simConnectData = parseRequestData(simData);
setTimeout(() => requestData(updateInterval), updateInterval);
}
else {
throw('Empty MSFS Touch Panel Server response.')
}
}
catch (error) {
console.log(error);
networkStatus = false;
setTimeout(() => requestData(SIMCONNECT_DATA_REQUEST_INTERVAL_SLOW), SIMCONNECT_DATA_REQUEST_INTERVAL_SLOW);
}
}
parseRequestData = (resultData) => {
if (resultData === []) return [];
let newData = [];
// Format value as specified by the data key as needed and apply defaults
resultData.forEach(item => {
if (item.javaScriptFormatting !== null) {
item.value = formattingMethod[item.javaScriptFormatting](item.value);
}
if (item.value === null || item.value === undefined) {
item.value = item.defaultValue;
}
newData[item.propName] = item.value;
})
return newData;
}
formattingMethod = {
toFixed0: (value) => {
return value.toFixed(0);
},
toFixed1: (value) => {
return value.toFixed(1);
},
toFixed2: (value) => {
return value.toFixed(2);
},
toFixed3: (value) => {
return value.toFixed(3);
},
toFixed4: (value) => {
return value.toFixed(4);
},
padStartZero4: (value) => {
return String(value).padStart(4, '0');
},
decToHex: (value) => {
let str = value.toString(16);
return str.substring(0, str.length - 4).padStart(4, '0');
},
toBlankIfNegative: (value) => {
if (value < 0)
return ''
return value;
},
toBlankIfZeroOrNegative: (value) => {
if (value <= 0)
return ''
return value;
},
toBoeingFlapsValue: (value) => {
value = value.toFixed(0)
switch (value) {
case '0':
return '0';
case '1':
return '1'
case '2':
return '2'
case '3':
return '5'
case '4':
return '10'
case '5':
return '15'
case '6':
return '25'
case '7':
return '30'
case '8':
return '40'
default:
return '5'
}
},
toBoeingElevatorTrimValue: (value) => {
return (value / 10).toFixed(2);
}
}

View file

@ -14,20 +14,31 @@ const SimConnectDataProvider = ({ children }) => {
// request data from SimConnect on timer interval
useEffect(() => {
let requestInterval = null;
let localSettings = localStorage.getItem('settings');
let updateInterval = localSettings !== null ? JSON.parse(localSettings).dataRefreshInterval : 500;
if (localSettings !== null)
requestInterval = JSON.parse(localStorage.getItem('settings')).dataRefreshInterval;
else
requestInterval = 500;
// Using sharedworker
// sharedWorker = new SharedWorker('/sharedworker.js');
// sharedWorker.port.start();
// sharedWorker.port.postMessage({apiUrl: API_URL.url, updateInterval: updateInterval});
// sharedWorker.port.onmessage = e => {
// if(e.data !== null && e.data !== undefined)
// setArduinoStatus(Boolean(e.data.arduinoStatus));
// setNetworkStatus(Boolean(e.data.networkStatus));
// if (e.data.systemEvent !== undefined && e.data.systemEvent !== null)
// setSimConnectSystemEvent(e.data.systemEvent.split('-')[0]);
// else
// setSimConnectSystemEvent(null);
// if(e.data.simConnectData !== undefined && e.data.simConnectData !== null)
// setSimConnectData(e.data.simConnectData);
// };
const requestData = async () => {
try {
let response = await fetch(`${API_URL.url}/getdata`).catch(() => {
handleConnectionError('MSFS Touch Panel Server is not available.')
return;
throw('MSFS Touch Panel Server is not available.')
});
if (response !== undefined) {
@ -36,62 +47,37 @@ const SimConnectDataProvider = ({ children }) => {
if (result === undefined)
throw new Error('MSFS Touch Panel Server error');
if (result.msfsStatus !== null && result.msfsStatus !== undefined)
setNetworkStatus(Boolean(result.msfsStatus));
else
setNetworkStatus(false);
setNetworkStatus(Boolean(result.msfsStatus ?? false));
setArduinoStatus(Boolean(result.arduinoStatus ?? false));
if (result.arduinoStatus !== null && result.arduinoStatus !== undefined)
setArduinoStatus(Boolean(result.arduinoStatus));
else
setArduinoStatus(false);
if (!result.msfsStatus)
throw('MSFS SimConnect is not available.')
if (result.systemEvent !== null && result.systemEvent !== undefined)
setSimConnectSystemEvent(result.systemEvent.split('-')[0]);
else
setSimConnectSystemEvent(null);
if (!result.msfsStatus)
{
handleConnectionError('MSFS SimConnect is not available.')
return;
}
var simData = JSON.parse(result.data ?? []);
if (result.data !== null && result.data !== undefined) {
var simData = JSON.parse(result.data);
if ((simData !== null && simData !== [])) {
if ((simData !== null && simData !== []))
setSimConnectData(parseRequestData(simData));
clearInterval(requestInterval);
let updateInterval = JSON.parse(localStorage.getItem('settings')).dataRefreshInterval;
requestInterval = setInterval(() => requestData(), updateInterval);
}
setTimeout(() => requestData(), updateInterval);
}
else {
clearInterval(requestInterval);
let updateInterval = JSON.parse(localStorage.getItem('settings')).dataRefreshInterval;
requestInterval = setInterval(() => requestData(), updateInterval);
}
throw('Empty MSFS Touch Panel Server response.')
}
}
catch (error) {
console.log(error);
setNetworkStatus(false);
handleConnectionError('MSFS Touch Panel Server is not available.')
setTimeout(() => requestData(), SIMCONNECT_DATA_REQUEST_INTERVAL_SLOW);
}
}
const handleConnectionError = (errorMessage) => {
console.error(errorMessage);
clearInterval(requestInterval);
requestInterval = setInterval(() => requestData(), SIMCONNECT_DATA_REQUEST_INTERVAL_SLOW); // slow down the request data interval until network reconnection
}
requestData();
return () => {
clearInterval(requestInterval);
}
}, [])
return (
@ -106,7 +92,8 @@ export default SimConnectDataProvider;
// custom hook
export const useSimConnectData = () => useContext(SimConnectDataContext);
export const simConnectGetPlanePanelProfilesInfo = async () => {
export const simConnectGetPlanePanelProfilesInfo = async () =>
{
try {
let response = await fetch(`${API_URL.url}/getplanepanelprofileinfo`);
let data = await response.json();

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.Shared</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.SimConnectAgent</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.TouchPanelAgent</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -9,21 +9,20 @@ namespace MSFSPopoutPanelManager.UserDataAgent
public AppSetting()
{
// Set defaults
AutoUpdaterUrl = "https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml";
LastUsedProfileId = -1;
MinimizeToTray = false;
AutoUpdaterUrl = "https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml";
AlwaysOnTop = true;
UseAutoPanning = true;
MinimizeAfterPopOut = false;
AutoPanningKeyBinding = "0";
MinimizeToTray = false;
StartMinimized = false;
AutoDisableTrackIR = true;
AutoPopOutPanels = true;
UseAutoPanning = true;
AutoPanningKeyBinding = "0";
MinimizeAfterPopOut = false;
OnScreenMessageDuration = 1;
UseLeftRightControlToPopOut = false;
IsEnabledTouchPanelServer = false;
AfterPopOutCameraView = new AfterPopOutCameraView();
AfterPopOutCameraView.PropertyChanged += (source, e) =>
{
@ -31,6 +30,10 @@ namespace MSFSPopoutPanelManager.UserDataAgent
OnPropertyChanged(arg.PropertyName, arg.OldValue, arg.NewValue);
};
AutoDisableTrackIR = true;
AutoResizeMsfsGameWindow = true;
TouchScreenSettings = new TouchScreenSettings();
TouchScreenSettings.PropertyChanged += (source, e) =>
{
@ -38,6 +41,7 @@ namespace MSFSPopoutPanelManager.UserDataAgent
OnPropertyChanged(arg.PropertyName, arg.OldValue, arg.NewValue);
};
IsEnabledTouchPanelServer = false;
TouchPanelSettings = new TouchPanelSettings();
TouchPanelSettings.PropertyChanged += (source, e) =>
{
@ -62,8 +66,6 @@ namespace MSFSPopoutPanelManager.UserDataAgent
public bool StartMinimized { get; set; }
public bool IncludeBuiltInPanel { get; set; }
public bool AutoPopOutPanels { get; set; }
public bool AutoDisableTrackIR { get; set; }
@ -74,6 +76,8 @@ namespace MSFSPopoutPanelManager.UserDataAgent
public bool IsEnabledTouchPanelServer { get; set; }
public bool AutoResizeMsfsGameWindow { get; set; }
public AfterPopOutCameraView AfterPopOutCameraView { get; set; }
public TouchScreenSettings TouchScreenSettings { get; set; }

View file

@ -14,6 +14,10 @@ namespace MSFSPopoutPanelManager.UserDataAgent
PanelSourceCoordinates = new ObservableCollection<PanelSourceCoordinate>();
TouchPanelBindings = new ObservableCollection<TouchPanelBinding>();
IsLocked = false;
PowerOnRequiredForColdStart = false;
IncludeInGamePanels = false;
MsfsGameWindowConfig = new MsfsGameWindowConfig();
// Legacy data
BindingAircraftLiveries = new ObservableCollection<string>();
@ -40,6 +44,10 @@ namespace MSFSPopoutPanelManager.UserDataAgent
public bool PowerOnRequiredForColdStart { get; set; }
public bool IncludeInGamePanels { get; set; }
public MsfsGameWindowConfig MsfsGameWindowConfig { get; set; }
[JsonIgnore]
public bool IsActive { get; set; }
@ -104,6 +112,9 @@ namespace MSFSPopoutPanelManager.UserDataAgent
[JsonIgnore]
public bool IsCustomPopOut { get { return PanelType == PanelType.CustomPopout; } }
[JsonIgnore]
public bool IsBuiltInPopOut { get { return PanelType == PanelType.BuiltInPopout; } }
[JsonIgnore]
public IntPtr PanelHandle { get; set; }
@ -147,4 +158,40 @@ namespace MSFSPopoutPanelManager.UserDataAgent
public string PanelId { get; set; }
}
public class MsfsGameWindowConfig : ObservableObject
{
public MsfsGameWindowConfig()
{
Top = 0;
Left = 0;
Width = 0;
Height = 0;
}
public MsfsGameWindowConfig(int left, int top, int width, int height)
{
Top = top;
Left = left;
Width = width;
Height = height;
}
public int Top { get; set; }
public int Left { get; set; }
public int Width { get; set; }
public int Height { get; set; }
[JsonIgnore]
public bool IsValid
{
get
{
return Width != 0 && Height != 0;
}
}
}
}

View file

@ -10,9 +10,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.UserDataAgent</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -1,6 +1,11 @@
# Version History
<hr/>
## Version 3.4.3
* Added ability to remember MSFS game window size and location for aircraft profile when running the game in windows display mode. This new setting is used to resize game window to match original size and location of MSFS game window when panel profile was defined initially. For existing aircraft profile, when running the game in windows display mode, the profile will automatically save MSFS game window position after the first successful pop out.
* Added ability to include in-game menu bar panels such as VFR Map, ATC, Checklist, etc to aircraft profile. During the pop out process, if any in-game menu bar panels are in popped out state, they will be included in panel configurations. This feature will only work if in-game menu bar panels are popped out initially and it also rely on MSFS re-opens these panels when flight starts (SU 10+).
* Added UI cue to show number circles momentarily when popping out panel to facilitate troubleshooting.
## Version 3.4.2
* Major change in how profile is bound to an aircraft. Previously, a profile is bound to an aircraft livery which requires you to activate binding when switching livery for the same aircraft. With this update, a profile is now bound to an aircraft so you no longer need to perform the binding step when switching livery. As you change active aircraft to fly, all existing livery binding will be automatically converted to aircraft binding if one exists. Also, a profile can still be bound to multiple aircrafts if you so choose such as when flying multiple variations of Cessna 172. This change has been a long awaited request to simplify your profile bindings.

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.WebServer</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -42,13 +42,15 @@ namespace MSFSPopoutPanelManager.WindowsAgent
public static void LeftClick(int x, int y)
{
PInvoke.SetCursorPos(x, y);
PInvoke.SetCursorPos(x, y); // Need to do this twice to overcome MSFS bug for separating pop out panels
PInvoke.SetCursorPos(x, y);
Thread.Sleep(300);
PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);
Thread.Sleep(200);
PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);
PInvoke.SetCursorPos(x + 5, y);
}
public static void LeftClickFast(int x, int y)

View file

@ -123,6 +123,9 @@ namespace MSFSPopoutPanelManager.WindowsAgent
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hwnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll")]
public static extern int ShowCursor(bool bShow);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool ShowWindowAsync(HandleRef hwnd, int nCmdShow);

View file

@ -1,4 +1,5 @@
using MSFSPopoutPanelManager.Shared;
using MSFSPopoutPanelManager.UserDataAgent;
using System;
using System.Collections.Generic;
using System.Drawing;
@ -98,6 +99,11 @@ namespace MSFSPopoutPanelManager.WindowsAgent
return PInvoke.FindWindowByCaption(IntPtr.Zero, caption);
}
public static string GetWindowCaption(IntPtr hwnd)
{
return PInvoke.GetWindowText(hwnd);
}
public static Rectangle GetClientRect(IntPtr hwnd)
{
Rectangle rectangle;
@ -136,6 +142,23 @@ namespace MSFSPopoutPanelManager.WindowsAgent
return count;
}
public static List<IntPtr> GetWindowsByPanelType(List<PanelType> panelTypes)
{
List<IntPtr> windowHandles = new List<IntPtr>();
PInvoke.EnumWindows((IntPtr hwnd, int lParam) =>
{
var panelType = GetWindowPanelType(hwnd);
if (panelTypes.Contains(panelType))
windowHandles.Add(hwnd);
return true;
}, 0);
return windowHandles;
}
public static PanelType GetWindowPanelType(IntPtr hwnd)
{
var className = PInvoke.GetClassName(hwnd);
@ -170,10 +193,70 @@ namespace MSFSPopoutPanelManager.WindowsAgent
var panelType = GetWindowPanelType(hwnd);
if (panelType == PanelType.CustomPopout || panelType == PanelType.MSFSTouchPanel)
WindowActionManager.CloseWindow(hwnd);
CloseWindow(hwnd);
return true;
}, 0);
}
public static MsfsGameWindowConfig GetMsfsGameWindowLocation()
{
var msfsGameWindowHandle = GetMsfsGameWindowHandle();
var isWindowedMode = IsMsfsGameInWindowedMode(msfsGameWindowHandle);
if (isWindowedMode)
{
var windowRect = GetWindowRect(msfsGameWindowHandle);
var clientRect = GetClientRect(msfsGameWindowHandle);
return new MsfsGameWindowConfig(windowRect.Left, windowRect.Top, clientRect.Width + 16, clientRect.Height + 39);
}
return new MsfsGameWindowConfig(0, 0, 0, 0);
}
public static void SetMsfsGameWindowLocation(MsfsGameWindowConfig msfsGameWindowConfig)
{
var msfsGameWindowHandle = GetMsfsGameWindowHandle();
var isWindowedMode = IsMsfsGameInWindowedMode(msfsGameWindowHandle);
if (isWindowedMode && msfsGameWindowConfig.IsValid)
PInvoke.MoveWindow(msfsGameWindowHandle, msfsGameWindowConfig.Left, msfsGameWindowConfig.Top, msfsGameWindowConfig.Width, msfsGameWindowConfig.Height, true);
}
public static bool IsMsfsGameInWindowedMode(IntPtr msfsGameWindowHandle)
{
if (msfsGameWindowHandle != IntPtr.Zero)
{
var currentStyle = (uint)PInvoke.GetWindowLong(msfsGameWindowHandle, PInvokeConstant.GWL_STYLE);
return (currentStyle & PInvokeConstant.WS_CAPTION) != 0;
}
return false;
}
public static IntPtr GetMsfsGameWindowHandle()
{
IntPtr msfsGameWindowHandle = IntPtr.Zero;
// Get game window handle
PInvoke.EnumWindows(new PInvoke.CallBack((IntPtr hwnd, int lParam) =>
{
var className = PInvoke.GetClassName(hwnd);
if (className == "AceApp") // MSFS windows designation
{
var caption = WindowsAgent.PInvoke.GetWindowText(hwnd);
if (caption.IndexOf("Microsoft Flight Simulator") > -1) // MSFS main game window
{
msfsGameWindowHandle = hwnd;
}
}
return true;
}), 0);
return msfsGameWindowHandle;
}
}
}

View file

@ -11,9 +11,9 @@
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.WindowsAgent</RootNamespace>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>Embedded</DebugType>
<Configurations>Debug;Release;DebugTouchPanel;ReleaseTouchPanel</Configurations>

View file

@ -8,7 +8,7 @@
xmlns:shared="clr-namespace:MSFSPopoutPanelManager.Shared;assembly=Shared"
mc:Ignorable="d"
Title="MSFS Pop Out Panel Manager"
Height="710"
Height="740"
Width="920"
WindowStartupLocation="CenterScreen"
ResizeMode="CanMinimize"
@ -72,7 +72,7 @@
</Image>
</DockPanel>
<DockPanel>
<StackPanel Name="panelSteps" DockPanel.Dock="Top" Height="545" Background="#FF323C64">
<StackPanel Name="panelSteps" DockPanel.Dock="Top" Height="575" Background="#FF323C64">
</StackPanel>
<WrapPanel DockPanel.Dock="Top" Orientation="Horizontal" Height="70" Background="#FF252523">
<Label Content="Status" Margin="5, 10, 5, 5"/>

View file

@ -10,6 +10,7 @@ namespace MSFSPopoutPanelManager.WpfApp
InitializeComponent();
this.Title = title;
this.txtMessage.Text = message;
this.Topmost = true;
}
private void btnDialogYes_Click(object sender, RoutedEventArgs e)

View file

@ -7,15 +7,15 @@
Title="PanelCoorOverlay"
ResizeMode="NoResize"
WindowStyle="None"
Height="46"
Width="55"
Height="30"
Width="30"
SizeToContent="WidthAndHeight"
Background="#01F0F0FF"
AllowsTransparency="True"
MouseMove="Window_MouseMove"
Loaded="Window_Loaded">
<Grid>
<Label Name="lblPanelIndex" Content="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Image Height="46" Width="55" HorizontalAlignment="Left" VerticalAlignment="Top" Opacity="1" Source="resources/transparent.png"/>
<Label Name="lblPanelIndex" Content="1" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="0,0,0,2"/>
<Image Height="30" Width="30" HorizontalAlignment="Left" VerticalAlignment="Top" Opacity="1" Source="resources/transparent.png"/>
</Grid>
</Window>

View file

@ -8,8 +8,8 @@ namespace MSFSPopoutPanelManager.WpfApp
{
public partial class PanelCoorOverlay : Window
{
private const int TOP_ADJUSTMENT = 23; // half of window height
private const int LEFT_ADJUSTMENT = 27; // half of window width
private const int TOP_ADJUSTMENT = 15; // half of window height
private const int LEFT_ADJUSTMENT = 15; // half of window width
private int _xCoor;
private int _yCoor;

View file

@ -44,7 +44,7 @@
</Window.Resources>
<Grid>
<DockPanel>
<TreeView Width="250" VerticalAlignment="Stretch" DockPanel.Dock="Left">
<TreeView Width="240" VerticalAlignment="Stretch" DockPanel.Dock="Left">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
@ -58,11 +58,12 @@
</Style.Resources>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem Header="Application Settings" Selected="TreeViewItem_Selected" Margin="0,15,0,10" IsSelected="True"></TreeViewItem>
<TreeViewItem Header="Pop Out Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Application Settings" Selected="TreeViewItem_Selected" Margin="0,15,0,10" IsSelected="True" Padding="0"></TreeViewItem>
<TreeViewItem Header="Auto Pop Out Panel Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Track IR Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Pop Out Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Touch Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Track IR Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Windowed Mode Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="MSFS Touch Panel Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10" Visibility="{Binding Path=AppSettingData.AppSetting.IsEnabledTouchPanelServer, Converter={StaticResource VisibleIfTrueConverter}, Mode=OneWay}"></TreeViewItem>
</TreeView>
<WrapPanel DockPanel.Dock="Right" Margin="20,10,0,0">
@ -96,6 +97,23 @@
<AccessText TextWrapping="Wrap">Start the application in minimized mode in system tray.</AccessText>
</CheckBox>
</WrapPanel>
</WrapPanel>
</ScrollViewer>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<WrapPanel Orientation="Vertical" Visibility="{Binding WindowedModeSettingsVisible, Converter={StaticResource VisibleIfTrueConverter}, Mode=OneWay}">
<WrapPanel Orientation="Vertical" Margin="0,0,20,20">
<TextBlock Style="{StaticResource TextBlockHeading}">Auto Resize MSFS Game Window (Used with Windowed Display Mode only)</TextBlock>
<Line Stretch="Fill" Stroke="Gray" X2="1"/>
<CheckBox IsChecked="{Binding AppSettingData.AppSetting.AutoResizeMsfsGameWindow, Mode=TwoWay}" >
<AccessText TextWrapping="Wrap">Enable automatic resize of MSFS game window when using Windowed Display Mode. When playing the game in Windowed Display Mode, this setting is used to resize game window to match original size
and location when panel profile was initially defined. When this setting is first checked, current game window size and location will also be saved automatically.</AccessText>
</CheckBox>
<TextBlock TextWrapping="Wrap" Margin="28,10,0,0" FontSize="14">
To override previously saved MSFS game window size and location, when editing panel location overlay, please also click Override Auto Panning Camera. This will not only save updated panel locations, it will also save MSFS
game window size and location to be used on subsequent pop out.
</TextBlock>
</WrapPanel>
</WrapPanel>
</ScrollViewer>
<ScrollViewer VerticalScrollBarVisibility="Auto">
@ -282,7 +300,7 @@
</WrapPanel>
<WrapPanel Visibility="{Binding AppSettingData.AppSetting.TouchPanelSettings.EnableTouchPanelIntegration, Converter={StaticResource VisibleIfTrueConverter}, Mode=OneWay}">
<TextBlock FontStyle="Italic">
Restart is require for all changes below to take effect.
Restart is required for all changes below to take effect.
</TextBlock>
<WrapPanel Orientation="Vertical" Margin="0,0,20,20">
<TextBlock Style="{StaticResource TextBlockHeading}">Enable Use of Arduino</TextBlock>

View file

@ -16,6 +16,8 @@ namespace MSFSPopoutPanelManager.WpfApp
InitializeComponent();
this.DataContext = preferencesViewModel;
_preferencesViewModel.Window = this;
}
public AppSetting AppSetting { get; set; }
@ -32,6 +34,8 @@ namespace MSFSPopoutPanelManager.WpfApp
public bool MSFSTouchPanelSettingsVisible { get; set; }
public bool WindowModeSettingsVisible { get; set; }
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
var treeViewItem = (TreeViewItem)e.Source;

View file

@ -3,7 +3,8 @@
"WpfApp": {
"commandName": "Project",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
"ASPNETCORE_ENVIRONMENT": "Development",
"ENABLE_XAML_DIAGNOSTICS_SOURCE_INFO": "1"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

@ -16,6 +16,6 @@
<SolidColorBrush Color="#FFF0F0F0" Opacity="0.01" />
</Window.Background>
<Grid>
<wv2:WebView2 Name="webView" DefaultBackgroundColor="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
<wv2:WebView2 Name="webView" DefaultBackgroundColor="Transparent" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Source=""/>
</Grid>
</mah:MetroWindow>

View file

@ -1,5 +1,5 @@
using MahApps.Metro.Controls;
using Microsoft.Web.WebView2.Wpf;
using Microsoft.Web.WebView2.Core;
using MSFSPopoutPanelManager.Shared;
using System;
using System.Runtime.InteropServices;
@ -11,8 +11,9 @@ namespace MSFSPopoutPanelManager.WpfApp
{
private string _planeId;
private string _panelId;
private CoreWebView2Environment _webView2Environment;
public TouchPanelWebViewDialog(string planeId, string panelId, string caption, int width, int height)
public TouchPanelWebViewDialog(string planeId, string panelId, string caption, int width, int height, CoreWebView2Environment environment)
{
InitializeComponent();
//this.Topmost = true;
@ -22,21 +23,22 @@ namespace MSFSPopoutPanelManager.WpfApp
_planeId = planeId;
_panelId = panelId;
_webView2Environment = environment;
Loaded += TouchPanelWebViewDialog_Loaded;
}
private void TouchPanelWebViewDialog_Loaded(object sender, System.Windows.RoutedEventArgs e)
private async void TouchPanelWebViewDialog_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
StartWebView(webView);
await webView.EnsureCoreWebView2Async(_webView2Environment);
// This somehow fixes webview did not maximize correctly when the host WPF dialog is maximized
WindowExtensions.FixWindowMaximizeCropping(this);
if (webView != null && webView.CoreWebView2 != null)
{
webView.CoreWebView2.Navigate($"{Constants.WEB_HOST_URI}/{_planeId.ToLower()}/{_panelId.ToLower()}");
}
private void StartWebView(WebView2 webView)
{
webView.Source = new Uri($"{Constants.WEB_HOST_URI}/{_planeId.ToLower()}/{_panelId.ToLower()}");
// This fixes webview which does not maximize correctly when host WPF dialog is maximized
WindowExtensions.FixWindowMaximizeCropping(this);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)

View file

@ -7,7 +7,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
mc:Ignorable="d"
Height="545"
Height="575"
Width="920"
Background="#FF323A64">
<UserControl.Resources>
@ -45,7 +45,7 @@
<Image Width="22" Height="22" VerticalAlignment="Center" Source="Resources\info_icon.png"></Image>
</controls:Tile>
</WrapPanel>
<DataGrid Name="PanelConfigGrid" HorizontalAlignment="Center" Width="882" Height="430" Margin="0 10 0 0" AutoGenerateColumns="False" CanUserResizeColumns="False" HorizontalScrollBarVisibility="Disabled"
<DataGrid Name="PanelConfigGrid" HorizontalAlignment="Center" Width="882" Height="460" Margin="0 10 0 0" AutoGenerateColumns="False" CanUserResizeColumns="False" HorizontalScrollBarVisibility="Disabled"
CanUserReorderColumns="False" CanUserResizeRows="False" HorizontalGridLinesBrush="#B9B9B9" VerticalGridLinesBrush="#B9B9B9" GridLinesVisibility="Horizontal" SelectionUnit="Cell"
BorderThickness="1" CanUserAddRows="False" CanUserSortColumns="False" KeyboardNavigation.TabNavigation="None" KeyboardNavigation.IsTabStop="False"
ItemsSource="{Binding ProfileData.ActiveProfile.PanelConfigs}" HeadersVisibility="Column" KeyboardNavigation.DirectionalNavigation="Local" MouseDown="PanelConfigGrid_MouseDown">
@ -211,7 +211,7 @@
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding HideTitlebar, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.ProfileData.ActiveProfile.IsLocked '}"
IsHitTestVisible="{c:Binding Path='!FullScreen and IsCustomPopOut'}"/>
IsHitTestVisible="{c:Binding Path='!FullScreen and (IsCustomPopOut or IsBuiltInPopOut)'}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@ -222,7 +222,7 @@
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding FullScreen, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.ProfileData.ActiveProfile.IsLocked'}"
IsHitTestVisible="{c:Binding Path='IsCustomPopOut'}"/>
IsHitTestVisible="{c:Binding Path='IsCustomPopOut or IsBuiltInPopOut'}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@ -233,7 +233,7 @@
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding TouchEnabled, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.ProfileData.ActiveProfile.IsLocked'}"
IsHitTestVisible="{c:Binding Path='!FullScreen and IsCustomPopOut'}"/>
IsHitTestVisible="{c:Binding Path='!FullScreen and (IsCustomPopOut or IsBuiltInPopOut)'}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
@ -244,7 +244,7 @@
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding DisableGameRefocus, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.ProfileData.ActiveProfile.IsLocked'}"
IsHitTestVisible="{c:Binding Path='!FullScreen and IsCustomPopOut and TouchEnabled'}"/>
IsHitTestVisible="{c:Binding Path='!FullScreen and (IsCustomPopOut or IsBuiltInPopOut) and TouchEnabled'}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

View file

@ -8,7 +8,7 @@
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
mc:Ignorable="d"
Height="545"
Height="575"
Width="920"
Background="#FF323A64">
<UserControl.Resources>
@ -77,6 +77,7 @@
<Button Content="-" ToolTip="Delete Binding" Margin="10,0,0,0" Width="40" Command="{Binding DeleteProfileBindingCommand}"/>
</WrapPanel>
<CheckBox Margin="10,5,0,0" Content="Power on required to pop out panels on cold start (G1000 / NXi Only)" IsChecked="{Binding ProfileData.ActiveProfile.PowerOnRequiredForColdStart}" Command="{Binding SetPowerOnRequiredCommand}" />
<CheckBox Margin="10,5,0,0" Content="Include in-game menu bar panels (VFR Map, Checklist, ATC, etc)" IsChecked="{Binding ProfileData.ActiveProfile.IncludeInGamePanels}" Command="{Binding SetIncludeInGamePanelsCommand}" />
<WrapPanel Name="TouchPanelConfigurationPanel" Orientation="Horizontal" Margin="0,10,0,0" Visibility="{Binding AppSettingData.AppSetting.IsEnabledTouchPanelServer, Converter={StaticResource VisibleIfTrueConverter}, Mode=OneWay}" >
<Label Content="Open MSFS touch panel when flight session starts" Margin="5,0,0,0" />
<Button Content="+" ToolTip="Add Binding" Margin="94,0,0,0" Width="40" Command="{Binding OpenTouchPanelBindingCommand}"/>
@ -114,7 +115,7 @@
</WrapPanel>
<DockPanel DockPanel.Dock="Right" Width="325" HorizontalAlignment="Center">
<Label DockPanel.Dock="Top" Content="Panel Locations" HorizontalAlignment="Center" Margin="0,10,0,0"/>
<DataGrid DockPanel.Dock="Top" HorizontalAlignment="Center" Width="290" Height="435" AutoGenerateColumns="False" CanUserResizeColumns="False" HorizontalScrollBarVisibility="Disabled"
<DataGrid DockPanel.Dock="Top" HorizontalAlignment="Center" Width="290" Height="465" AutoGenerateColumns="False" CanUserResizeColumns="False" HorizontalScrollBarVisibility="Disabled"
CanUserReorderColumns="False" CanUserResizeRows="False" IsReadOnly="True" HorizontalGridLinesBrush="#B9B9B9" VerticalGridLinesBrush="#B9B9B9" GridLinesVisibility="Horizontal" BorderThickness="1"
CanUserAddRows="False" CanUserSortColumns="False" ItemsSource="{Binding ProfileData.ActiveProfile.PanelSourceCoordinates}">
<DataGrid.CellStyle>

View file

@ -16,6 +16,16 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
return (bool)dialog.ShowDialog();
}
public static bool ConfirmDialog(string title, string message, Window owner)
{
var dialog = new ConfirmationDialog(title, message);
dialog.Owner = owner;
dialog.Topmost = true;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
return (bool)dialog.ShowDialog();
}
public static AddProfileDialogResult AddProfileDialog(List<Profile> profiles)
{
var dialog = new AddProfileDialog(profiles);

View file

@ -1,4 +1,5 @@
using MSFSPopoutPanelManager.Orchestration;
using Microsoft.Web.WebView2.Core;
using MSFSPopoutPanelManager.Orchestration;
using MSFSPopoutPanelManager.Shared;
using MSFSPopoutPanelManager.UserDataAgent;
using MSFSPopoutPanelManager.WindowsAgent;
@ -12,12 +13,13 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{
private MainOrchestrator _orchestrator;
private bool _minimizeForPopOut;
private CoreWebView2Environment _coreWebView2Environment;
public OrchestratorHelper(MainOrchestrator orchestrator)
{
_orchestrator = orchestrator;
_orchestrator.PanelSource.onOverlayShowed += HandleShowOverlay;
_orchestrator.PanelSource.OnOverlayShowed += HandleShowOverlay;
_orchestrator.PanelSource.OnLastOverlayRemoved += (sender, e) => HandleRemovePanelSourceOverlay(false);
_orchestrator.PanelSource.OnAllOverlaysRemoved += (sender, e) => HandleRemovePanelSourceOverlay(true);
_orchestrator.PanelSource.OnSelectionStarted += HandlePanelSelectionStarted;
@ -26,6 +28,7 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
_orchestrator.PanelPopOut.OnPopOutStarted += HandleOnPopOutStarted;
_orchestrator.PanelPopOut.OnPopOutCompleted += HandleOnPopOutCompleted;
_orchestrator.PanelPopOut.OnTouchPanelOpened += HandleOnTouchPanelOpened;
_orchestrator.PanelPopOut.OnPanelSourceOverlayFlashed += HandleOnPanelSourceOverlayFlashed;
StatusMessageWriter.OnStatusMessage += HandleOnStatusMessage;
}
@ -72,6 +75,16 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
AddPanelCoorOverlay(e);
}
private void HandleOnPanelSourceOverlayFlashed(object sender, PanelSourceCoordinate e)
{
Application.Current.Dispatcher.Invoke(() =>
{
AddPanelCoorOverlay(e);
System.Threading.Thread.Sleep(750);
HandleRemovePanelSourceOverlay(true);
});
}
private void HandleRemovePanelSourceOverlay(bool removeAll)
{
Application.Current.Dispatcher.Invoke(() =>
@ -113,9 +126,15 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
private void HandleOnTouchPanelOpened(object sender, TouchPanelOpenEventArg e)
{
Application.Current.Dispatcher.Invoke(() =>
Application.Current.Dispatcher.Invoke(async () =>
{
TouchPanelWebViewDialog window = new TouchPanelWebViewDialog(e.PlaneId, e.PanelId, e.Caption, e.Width, e.Height);
if (_coreWebView2Environment == null)
{
var options = new CoreWebView2EnvironmentOptions("--disable-web-security");
_coreWebView2Environment = await CoreWebView2Environment.CreateAsync(null, null, options);
}
TouchPanelWebViewDialog window = new TouchPanelWebViewDialog(e.PlaneId, e.PanelId, e.Caption, e.Width, e.Height, _coreWebView2Environment);
window.Show();
});
}

View file

@ -45,17 +45,23 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
.ObservesProperty(() => ProfileData.ActiveProfile)
.ObservesProperty(() => FlightSimData.IsSimulatorStarted);
SetIncludeInGamePanelsCommand = new DelegateCommand(() => ProfileData.WriteProfiles(), () => FlightSimData.HasCurrentMsfsAircraft && ProfileData.HasActiveProfile && FlightSimData.IsSimulatorStarted)
.ObservesProperty(() => FlightSimData.HasCurrentMsfsAircraft)
.ObservesProperty(() => ProfileData.ActiveProfile)
.ObservesProperty(() => FlightSimData.IsSimulatorStarted);
StartPanelSelectionCommand = new DelegateCommand(OnStartPanelSelection, () => FlightSimData.HasCurrentMsfsAircraft && ProfileData.HasActiveProfile && ProfileData.ActiveProfile != null && FlightSimData.IsSimulatorStarted && FlightSimData.IsInCockpit)
.ObservesProperty(() => FlightSimData.HasCurrentMsfsAircraft)
.ObservesProperty(() => ProfileData.ActiveProfile)
.ObservesProperty(() => FlightSimData.IsSimulatorStarted)
.ObservesProperty(() => FlightSimData.IsInCockpit);
StartPopOutCommand = new DelegateCommand(OnStartPopOut, () => FlightSimData.HasCurrentMsfsAircraft && ProfileData.HasActiveProfile && (ProfileData.ActiveProfile.PanelSourceCoordinates.Count > 0 || ProfileData.ActiveProfile.TouchPanelBindings.Count > 0) && FlightSimData.IsSimulatorStarted && FlightSimData.IsInCockpit)
StartPopOutCommand = new DelegateCommand(OnStartPopOut, () => FlightSimData.HasCurrentMsfsAircraft && ProfileData.HasActiveProfile && (ProfileData.ActiveProfile.PanelSourceCoordinates.Count > 0 || ProfileData.ActiveProfile.TouchPanelBindings.Count > 0 || ProfileData.ActiveProfile.IncludeInGamePanels) && FlightSimData.IsSimulatorStarted && FlightSimData.IsInCockpit)
.ObservesProperty(() => FlightSimData.HasCurrentMsfsAircraft)
.ObservesProperty(() => ProfileData.ActiveProfile)
.ObservesProperty(() => ProfileData.ActiveProfile.PanelSourceCoordinates.Count)
.ObservesProperty(() => ProfileData.ActiveProfile.TouchPanelBindings.Count)
.ObservesProperty(() => ProfileData.ActiveProfile.IncludeInGamePanels)
.ObservesProperty(() => FlightSimData.IsSimulatorStarted)
.ObservesProperty(() => FlightSimData.IsInCockpit);
@ -94,6 +100,8 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
public DelegateCommand SetPowerOnRequiredCommand { get; private set; }
public DelegateCommand SetIncludeInGamePanelsCommand { get; private set; }
public DelegateCommand StartPanelSelectionCommand { get; private set; }
public DelegateCommand StartPopOutCommand { get; private set; }

View file

@ -1,6 +1,7 @@
using MSFSPopoutPanelManager.Orchestration;
using MSFSPopoutPanelManager.Shared;
using Prism.Commands;
using System.Windows;
namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{
@ -20,6 +21,8 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
SectionSelectCommand = new DelegateCommand<object>(OnSectionSelected);
}
public Window Window { get; set; }
public DelegateCommand<object> SectionSelectCommand { get; private set; }
public AppSettingData AppSettingData { get { return _orchestrator.AppSettingData; } }
@ -36,6 +39,8 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
public bool MSFSTouchPanelSettingsVisible { get; private set; }
public bool WindowedModeSettingsVisible { get; private set; }
private void OnSectionSelected(object commandParameter)
{
ApplicationSettingsVisible = false;
@ -44,6 +49,7 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
TrackIRSettingsVisible = false;
TouchSettingsVisible = false;
MSFSTouchPanelSettingsVisible = false;
WindowedModeSettingsVisible = false;
switch (commandParameter.ToString())
{
@ -65,6 +71,9 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
case "MSFS Touch Panel Settings":
MSFSTouchPanelSettingsVisible = true;
break;
case "Windowed Mode Settings":
WindowedModeSettingsVisible = true;
break;
}
}
}

View file

@ -14,9 +14,9 @@
<RootNamespace>MSFSPopoutPanelManager.WpfApp</RootNamespace>
<ApplicationIcon>logo.ico</ApplicationIcon>
<Platforms>x64</Platforms>
<Version>3.4.2.0</Version>
<AssemblyVersion>3.4.2.0</AssemblyVersion>
<FileVersion>3.4.2.0</FileVersion>
<Version>3.4.3.0</Version>
<AssemblyVersion>3.4.3.0</AssemblyVersion>
<FileVersion>3.4.3.0</FileVersion>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<DebugType>embedded</DebugType>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
@ -69,13 +69,13 @@
<ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.0" />
<PackageReference Include="CalcBinding" Version="2.5.2" />
<PackageReference Include="CoordinateSharp" Version="2.13.1.1" Condition=" '$(Configuration)' == 'ReleaseTouchPanel' "/>
<PackageReference Include="CoordinateSharp" Version="2.13.1.1" Condition=" '$(Configuration)' == 'ReleaseTouchPanel' " />
<PackageReference Include="Fody" Version="6.6.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />
<PackageReference Include="ini-parser-netcore3.1" Version="3.0.0" Condition=" '$(Configuration)' == 'ReleaseTouchPanel' "/>
<PackageReference Include="ini-parser-netcore3.1" Version="3.0.0" Condition=" '$(Configuration)' == 'ReleaseTouchPanel' " />
<PackageReference Include="InputSimulatorCore" Version="1.0.5" />
<PackageReference Include="log4net" Version="2.0.14" />
<PackageReference Include="MahApps.Metro" Version="2.4.9" />
@ -89,7 +89,7 @@
<PackageReference Include="WindowsHook" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.SpaServices" Version="3.1.27" Condition=" '$(Configuration)' == 'ReleaseTouchPanel' Or '$(Configuration)' == 'DebugTouchPanel' " />
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="6.0.7" Condition=" '$(Configuration)' == 'ReleaseTouchPanel' Or '$(Configuration)' == 'DebugTouchPanel' " />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1264.42" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1293.44" />
</ItemGroup>
<ItemGroup>

View file

@ -1,39 +1,21 @@
Version 3.4.2.0
Version 3.4.3.0
This release is optional and please feel to skip this update.
Change Log:
* Major change in how profile is bound to an aircraft. Previously, a profile is bound to an
aircraft livery which requires you to activate binding when switching livery for the same
aircraft. With this update, a profile is now bound to an aircraft so you no longer need to
perform the binding step when switching livery. As you change active aircraft to fly, all
existing livery binding will be automatically converted to aircraft binding if one exists.
Also, a profile can still be bound to multiple aircrafts if you so choose such as when
flying multiple variations of Cessna 172. This change has been a long awaited request to
simplify your profile bindings.
* Added ability to remember MSFS game window size and location for aircraft profile when
running the game in windows display mode. This new setting is used to resize game window to
match original size and location of MSFS game window when panel profile was defined
initially. For existing aircraft profile, when running the game in windows display mode,
the profile will automatically save MSFS game window position after the first successful
pop out.
* Added auto assignment of aircraft binding for a newly created profile if the active
aircraft has no previous profile binding specified.
* Added ability to include in-game menu bar panels such as VFR Map, ATC, Checklist, etc to
aircraft profile. During the pop out process, if any in-game menu bar panels are in popped
out state, they will be included in panel configurations. This feature will only work if
in-game menu bar panels are popped out initially and it also rely on MSFS re-opens these
panels when flight starts (SU 10+).
* Added new keyboard shortcuts to move and resize pop outs. Please click on a new
information icon in the upper right corner of panel configuration screen for instruction in
how to use these new keyboard shortcuts.
* Added new setting to minimize pop out manager after panels have been popped out.
* Added work around for SU10 Beta issue when after panel separations, panels' size become
so big and they block most of the game window for lower resolution screen and prevented Pop
Out Panel Manager from popping out the next panel.
* Made improvements to how panels are separated during pop out process.
* Fixed an issue when adjusting position and size of a pop out panel on some PC
configuration.
Known issue:
* When changing the width or height of a pop up that has Hide Title Bar enable, it will
sometime break the Hide Title Bar setting and the only way to fix this is to re-pop out the
panel. Currently, this is a bug in MSFS in how it handles the sizing and rendering of pop
outs window.
* Added UI cue to show number circles momentarily when popping out panel to facilitate
troubleshooting.