Merge pull request #276 from jp64k/RSL-3.0-2

Several changes and fixes, see notes below
This commit is contained in:
Fenopy
2025-12-23 07:07:17 -06:00
committed by GitHub
4 changed files with 334 additions and 121 deletions

View File

@@ -170,7 +170,7 @@ namespace JR.Utils.GUI.Forms
titleLabel.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold);
titleLabel.Location = new System.Drawing.Point(0, 0);
titleLabel.Name = "titleLabel";
titleLabel.Padding = new System.Windows.Forms.Padding(18, 0, 0, 0);
titleLabel.Padding = new System.Windows.Forms.Padding(12, 0, 0, 0);
titleLabel.Size = new System.Drawing.Size(218, 28);
titleLabel.TabIndex = 0;
titleLabel.Text = "<Caption>";
@@ -198,7 +198,7 @@ namespace JR.Utils.GUI.Forms
//
button1.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
button1.DialogResult = System.Windows.Forms.DialogResult.OK;
button1.Location = new System.Drawing.Point(16, 80);
button1.Location = new System.Drawing.Point(26, 80);
button1.Name = "button1";
button1.Size = new System.Drawing.Size(75, 28);
button1.TabIndex = 2;
@@ -222,7 +222,7 @@ namespace JR.Utils.GUI.Forms
richTextBoxMessage.BorderStyle = System.Windows.Forms.BorderStyle.None;
richTextBoxMessage.DataBindings.Add(new System.Windows.Forms.Binding("Text", FlexibleMessageBoxFormBindingSource, "MessageText", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged));
richTextBoxMessage.Font = new System.Drawing.Font("Microsoft Sans Serif", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0);
richTextBoxMessage.Location = new System.Drawing.Point(52, 6);
richTextBoxMessage.Location = new System.Drawing.Point(46, 6);
richTextBoxMessage.Margin = new System.Windows.Forms.Padding(0);
richTextBoxMessage.Name = "richTextBoxMessage";
richTextBoxMessage.ReadOnly = true;
@@ -259,7 +259,7 @@ namespace JR.Utils.GUI.Forms
//
button2.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
button2.DialogResult = System.Windows.Forms.DialogResult.OK;
button2.Location = new System.Drawing.Point(97, 80);
button2.Location = new System.Drawing.Point(107, 80);
button2.Name = "button2";
button2.Size = new System.Drawing.Size(75, 28);
button2.TabIndex = 3;
@@ -277,7 +277,7 @@ namespace JR.Utils.GUI.Forms
//
button3.Anchor = System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right;
button3.DialogResult = System.Windows.Forms.DialogResult.OK;
button3.Location = new System.Drawing.Point(178, 80);
button3.Location = new System.Drawing.Point(188, 80);
button3.Name = "button3";
button3.Size = new System.Drawing.Size(75, 28);
button3.TabIndex = 0;
@@ -843,6 +843,11 @@ namespace JR.Utils.GUI.Forms
flexibleMessageBoxForm.richTextBoxMessage.Font = FONT;
SetDialogSizes(flexibleMessageBoxForm, text, caption);
// Force panel resize to fix closebutton position
int contentWidth = flexibleMessageBoxForm.ClientSize.Width - 16; // 8px padding
flexibleMessageBoxForm.titlePanel.Width = contentWidth;
SetDialogStartPosition(flexibleMessageBoxForm, owner);
return flexibleMessageBoxForm.ShowDialog(owner);

View File

@@ -52,7 +52,7 @@ public class FastGalleryPanel : Control
// Interaction
private int _hoveredIndex = -1;
private int _selectedIndex = -1;
public int _selectedIndex = -1;
private bool _isHoveringDeleteButton = false;
// Context Menu & Favorites

View File

@@ -882,24 +882,10 @@ namespace AndroidSideloader
public async Task<int> CheckForDevice()
{
Devices.Clear();
string output = string.Empty;
string error = string.Empty;
string battery = string.Empty;
ADB.DeviceID = GetDeviceID();
Thread t1 = new Thread(() =>
{
output = ADB.RunAdbCommandToString("devices").Output;
});
t1.Start();
while (t1.IsAlive)
{
await Task.Delay(100);
}
string output = await Task.Run(() => ADB.RunAdbCommandToString("devices").Output); // Run off UI thread
string[] line = output.Split('\n');
int i = 0;
devicesComboBox.Items.Clear();
@@ -909,9 +895,10 @@ namespace AndroidSideloader
{
if (i > 0 && currLine.Length > 0)
{
Devices.Add(currLine.Split(' ')[0]);
_ = devicesComboBox.Items.Add(currLine.Split(' ')[0]);
_ = Logger.Log(currLine.Split(' ')[0] + "\n", LogLevel.INFO, false);
string deviceId = currLine.Split('\t')[0];
Devices.Add(deviceId);
_ = devicesComboBox.Items.Add(deviceId);
_ = Logger.Log(deviceId + "\n", LogLevel.INFO, false);
}
Debug.WriteLine(currLine);
i++;
@@ -920,14 +907,13 @@ namespace AndroidSideloader
if (devicesComboBox.Items.Count > 0)
{
devicesComboBox.SelectedIndex = 0;
string battery = await Task.Run(() => ADB.RunAdbCommandToString("shell dumpsys battery").Output); // Run off UI thread
battery = Utilities.StringUtilities.RemoveEverythingBeforeFirst(battery, "level:");
battery = Utilities.StringUtilities.RemoveEverythingAfterFirst(battery, "\n");
battery = Utilities.StringUtilities.KeepOnlyNumbers(battery);
batteryLabel.Text = battery;
}
battery = ADB.RunAdbCommandToString("shell dumpsys battery").Output;
battery = Utilities.StringUtilities.RemoveEverythingBeforeFirst(battery, "level:");
battery = Utilities.StringUtilities.RemoveEverythingAfterFirst(battery, "\n");
battery = Utilities.StringUtilities.KeepOnlyNumbers(battery);
batteryLabel.Text = battery;
UpdateQuestInfoPanel();
return devicesComboBox.SelectedIndex;
@@ -3089,13 +3075,10 @@ Additional Thanks & Resources
await Task.Delay(100);
}
// Reset the initialized flag so initListView rebuilds _allItems with current install status
_allItemsInitialized = false;
_galleryDataSource = null;
initListView(false);
isLoading = false;
// Use RefreshGameListAsync to preserve filter state
await RefreshGameListAsync();
changeTitle("");
}
@@ -4060,27 +4043,67 @@ If the problem persists, visit our Telegram (https://t.me/VRPirates) or Discord
private async void ADBWirelessToggle_Click(object sender, EventArgs e)
{
// Check if wireless ADB is currently enabled
bool isWirelessEnabled = File.Exists(storedIpPath) && !string.IsNullOrEmpty(settings.IPAddress);
// Check if wireless ADB is currently enabled by verifying actual connection
bool isWirelessEnabled = false;
if (File.Exists(storedIpPath) && !string.IsNullOrEmpty(settings.IPAddress))
{
// Verify we're actually connected wirelessly by checking connected devices
string devicesOutput = ADB.RunAdbCommandToString("devices").Output;
string[] lines = devicesOutput.Split('\n');
foreach (string line in lines)
{
// Wireless devices show as IP:port format (e.g., "192.168.1.100:5555")
if (line.Contains(":5555") && line.Contains("device"))
{
isWirelessEnabled = true;
break;
}
}
}
// If enabled, offer to disable or switch device
if (isWirelessEnabled)
{
DialogResult dialogResult = FlexibleMessageBox.Show(
Program.form,
"Wireless ADB is currently enabled.\n\n" +
"Yes = Connect to a different device\n" +
"No = Disable wireless ADB completely",
"Wireless ADB Options",
MessageBoxButtons.YesNoCancel);
if (dialogResult == DialogResult.Cancel)
string action = null;
using (Form dialog = new Form())
{
return;
dialog.Text = "Wireless ADB Options";
dialog.Size = new Size(386, 130);
dialog.StartPosition = FormStartPosition.CenterParent;
dialog.FormBorderStyle = FormBorderStyle.FixedDialog;
dialog.MaximizeBox = false;
dialog.MinimizeBox = false;
dialog.BackColor = Color.FromArgb(20, 24, 29);
dialog.ForeColor = Color.White;
var label = new Label
{
Text = "A device is currently connected via Wireless ADB.",
ForeColor = Color.White,
AutoSize = true,
Location = new Point(15, 15)
};
var btnSwitch = CreateStyledButton("Connect New Device", DialogResult.None, new Point(15, 45));
btnSwitch.Size = new Size(170, 32);
btnSwitch.Click += (s, ev) => { action = "switch"; dialog.DialogResult = DialogResult.OK; };
var btnDisable = CreateStyledButton("Disable Wireless ADB", DialogResult.None, new Point(195, 45));
btnDisable.Size = new Size(160, 32);
btnDisable.Click += (s, ev) => { action = "disable"; dialog.DialogResult = DialogResult.OK; };
dialog.Controls.AddRange(new Control[] { label, btnSwitch, btnDisable });
if (dialog.ShowDialog(this) != DialogResult.OK || action == null)
{
return;
}
}
// Disable wireless ADB completely
if (dialogResult == DialogResult.No)
if (action == "disable")
{
ADB.wirelessadbON = false;
changeTitle("Disabling wireless ADB...");
@@ -4112,38 +4135,173 @@ If the problem persists, visit our Telegram (https://t.me/VRPirates) or Discord
return;
}
// User chose "Yes" switch device: disconnect current wireless connection
// User chose to switch device: disconnect current wireless connection
changeTitle("Disconnecting current device...");
await Task.Run(() => ADB.RunAdbCommandToString("disconnect"));
}
// Enable or switch wireless ADB - offer scan or manual entry
DialogResult res = FlexibleMessageBox.Show(
Program.form,
"How would you like to connect?\n\n" +
"Yes = Automatic (scans network to find device)\n" +
"No = Manual (enter IP address)",
"Connection Method",
MessageBoxButtons.YesNoCancel);
if (res == DialogResult.Cancel)
// Connect: Show custom dialog with three options
string connectionMethod = null;
using (Form dialog = new Form())
{
changeTitle("");
return;
dialog.Text = "Wireless ADB Connection";
dialog.Size = new Size(456, 130);
dialog.StartPosition = FormStartPosition.CenterParent;
dialog.FormBorderStyle = FormBorderStyle.FixedDialog;
dialog.MaximizeBox = false;
dialog.MinimizeBox = false;
dialog.BackColor = Color.FromArgb(20, 24, 29);
dialog.ForeColor = Color.White;
var label = new Label
{
Text = "How would you like to connect?",
ForeColor = Color.White,
AutoSize = true,
Location = new Point(15, 15)
};
var btnScan = CreateStyledButton("Automatic", DialogResult.None, new Point(15, 45));
btnScan.Size = new Size(130, 32);
btnScan.Click += (s, ev) => { connectionMethod = "scan"; dialog.DialogResult = DialogResult.OK; };
var btnUSB = CreateStyledButton("Automatic (USB)", DialogResult.None, new Point(155, 45));
btnUSB.Size = new Size(130, 32);
btnUSB.Click += (s, ev) => { connectionMethod = "usb"; dialog.DialogResult = DialogResult.OK; };
var btnManual = CreateStyledButton("Manual", DialogResult.None, new Point(295, 45));
btnManual.Size = new Size(130, 32);
btnManual.Click += (s, ev) => { connectionMethod = "manual"; dialog.DialogResult = DialogResult.OK; };
dialog.Controls.AddRange(new Control[] { label, btnScan, btnUSB, btnManual });
if (dialog.ShowDialog(this) != DialogResult.OK || connectionMethod == null)
{
changeTitle("");
return;
}
}
string ipAddress = null;
if (res == DialogResult.Yes)
if (connectionMethod == "scan")
{
// Network scan
ipAddress = await ShowNetworkScanDialogAsync();
}
else
else if (connectionMethod == "manual")
{
// Manual IP entry
ipAddress = ShowManualIPDialog();
}
else if (connectionMethod == "usb")
{
// Setup via USB
DialogResult usbResult = FlexibleMessageBox.Show(
Program.form,
"Please make sure your Quest is connected to your PC via USB, then click OK.\n" +
"If you need more time, click Cancel and return when you're ready.",
"Connect Your Quest",
MessageBoxButtons.OKCancel);
if (usbResult == DialogResult.Cancel)
{
changeTitle("");
return;
}
changeTitle("Setting up wireless ADB via USB...");
progressBar.IsIndeterminate = true;
progressBar.OperationType = "";
// Check for device and enable TCP/IP mode
await Task.Run(() =>
{
_ = ADB.RunAdbCommandToString("devices");
_ = ADB.RunAdbCommandToString("tcpip 5555");
});
_ = FlexibleMessageBox.Show(
Program.form,
"Click OK to retrieve your Quest's local IP address.",
"Get Local IP Address",
MessageBoxButtons.OK);
await Task.Delay(1000);
// Get IP address
changeTitle("Retrieving IP address...");
string input = await Task.Run(() => ADB.RunAdbCommandToString("shell ip route").Output);
string[] strArrayOne = input.Split(' ');
if (strArrayOne.Length > 8 && strArrayOne[0].Length > 7)
{
string IPaddr = strArrayOne[8];
string IPcmnd = "connect " + IPaddr + ":5555";
_ = FlexibleMessageBox.Show(
Program.form,
$"Your Quest's local IP address is: {IPaddr}\n\n" +
$"Please disconnect your Quest and wait 2-3 seconds.\n" +
$"Once that's done, click OK.",
"Disconnect USB",
MessageBoxButtons.OK);
changeTitle("Connecting wirelessly...");
await Task.Delay(2000);
// Attempt wireless connection
await Task.Run(() => ADB.RunAdbCommandToString(IPcmnd));
await Task.Delay(2000);
// Verify device is actually connected
int deviceIndex = await CheckForDevice();
// success
if (deviceIndex >= 0)
{
// Update UI with device info
await Task.Run(() =>
{
changeTitlebarToDevice();
showAvailableSpace();
});
settings.IPAddress = IPcmnd;
settings.WirelessADB = true;
settings.Save();
try { File.WriteAllText(storedIpPath, IPcmnd); }
catch (Exception ex) { Logger.Log($"Unable to write to StoredIP.txt: {ex.Message}", LogLevel.ERROR); }
ADB.wirelessadbON = true;
// Configure WiFi wakeup settings
await Task.Run(() =>
{
_ = ADB.RunAdbCommandToString("shell settings put global wifi_wakeup_available 1");
_ = ADB.RunAdbCommandToString("shell settings put global wifi_wakeup_enabled 1");
});
progressBar.IsIndeterminate = false;
changeTitle("Connected successfully!", true);
UpdateWirelessADBButtonText();
UpdateStatusLabels();
}
// failure
else
{
progressBar.IsIndeterminate = false;
changeTitle("");
_ = FlexibleMessageBox.Show(Program.form, "Device connection failed! Connect Quest via USB and try again.");
}
}
else
{
progressBar.IsIndeterminate = false;
changeTitle("");
_ = FlexibleMessageBox.Show(Program.form, "Device connection failed! Connect Quest via USB and try again.");
}
return;
}
if (string.IsNullOrEmpty(ipAddress))
{
@@ -4649,13 +4807,9 @@ If the problem persists, visit our Telegram (https://t.me/VRPirates) or Discord
}
changeTitle("Refreshing games list...");
// Reset the initialized flag so initListView rebuilds _allItems with current install status
_allItemsInitialized = false;
_galleryDataSource = null;
listAppsBtn();
initListView(false);
// Use RefreshGameListAsync to preserve filter state
_ = RefreshGameListAsync();
}
bool dialogIsUp = false;
if (keyData == Keys.F1 && !dialogIsUp)
@@ -5182,13 +5336,7 @@ function onYouTubeIframeAPIReady() {
}
changeTitle("Refreshing installed apps and checking for updates...");
// Reset the initialized flag so initListView rebuilds _allItems with current install status
_allItemsInitialized = false;
_galleryDataSource = null;
listAppsBtn();
initListView(false);
if (SideloaderRCLONE.games.Count < 1)
{
@@ -5196,6 +5344,9 @@ function onYouTubeIframeAPIReady() {
"There are no games in rclone, please check your internet connection and verify the config is working properly.");
return;
}
// Use RefreshGameListAsync to preserve filter state
_ = RefreshGameListAsync();
}
private void gamesListView_MouseDoubleClick(object sender, MouseEventArgs e)
@@ -5741,6 +5892,12 @@ function onYouTubeIframeAPIReady() {
_fastGallery.UpdateItems(_allItems);
}
}
// Clear other filter states
updateAvailableClicked = false;
upToDate_Clicked = false;
NeedsDonation_Clicked = false;
UpdateFilterButtonStates();
}
public async void UpdateQuestInfoPanel()
@@ -7163,6 +7320,26 @@ function onYouTubeIframeAPIReady() {
bool wasUpdateAvailableClicked = updateAvailableClicked;
bool wasUpToDateClicked = upToDate_Clicked;
bool wasNeedsDonationClicked = NeedsDonation_Clicked;
bool wasFavoritesView = favoriteSwitcher.Text == "ALL";
// Save the currently selected package name to restore after refresh
string selectedPackageName = null;
if (gamesListView.SelectedItems.Count > 0)
{
var selectedItem = gamesListView.SelectedItems[0];
if (selectedItem.SubItems.Count > 2)
{
selectedPackageName = selectedItem.SubItems[2].Text;
}
}
else if (isGalleryView && _fastGallery != null)
{
var galleryItem = _fastGallery.GetItemAtIndex(_fastGallery._selectedIndex);
if (galleryItem != null && galleryItem.SubItems.Count > 2)
{
selectedPackageName = galleryItem.SubItems[2].Text;
}
}
// Temporarily clear filter states
updateAvailableClicked = false;
@@ -7188,6 +7365,13 @@ function onYouTubeIframeAPIReady() {
isGalleryView = wasGalleryView;
// Reapply the active filter
if (wasFavoritesView)
{
// Reapply favorites filter
favoriteSwitcher.Text = "FAVORITES"; // Reset text first
favoriteSwitcher_Click(favoriteSwitcher, EventArgs.Empty); // This will toggle to show favorites and set text to "ALL"
}
if (wasUpToDateClicked)
{
upToDate_Clicked = true;
@@ -7209,8 +7393,38 @@ function onYouTubeIframeAPIReady() {
gamesGalleryView.Visible = true;
PopulateGalleryView();
}
// Restore selection and scroll to the previously selected item
if (!string.IsNullOrEmpty(selectedPackageName))
{
RestoreSelectionByPackageName(selectedPackageName);
}
}
private void RestoreSelectionByPackageName(string packageName)
{
if (string.IsNullOrEmpty(packageName))
return;
// Restore in ListView
foreach (ListViewItem item in gamesListView.Items)
{
if (item.SubItems.Count > 2 &&
item.SubItems[2].Text.Equals(packageName, StringComparison.OrdinalIgnoreCase))
{
item.Selected = true;
item.Focused = true;
item.EnsureVisible();
break;
}
}
// Restore in Gallery view
if (isGalleryView && _fastGallery != null)
{
_fastGallery.ScrollToPackage(packageName);
}
}
private async void timer_DeviceCheck(object sender, EventArgs e)
{
// Skip if a device is connected, we're in the middle of loading or other operations
@@ -7241,13 +7455,9 @@ function onYouTubeIframeAPIReady() {
await CheckForDevice();
changeTitlebarToDevice();
showAvailableSpace();
// Reset the initialized flag so initListView rebuilds _allItems with current install status
_allItemsInitialized = false;
_galleryDataSource = null;
listAppsBtn();
initListView(false);
// Use RefreshGameListAsync to preserve filter state
await RefreshGameListAsync();
UpdateStatusLabels();
}
}

View File

@@ -76,13 +76,10 @@ namespace AndroidSideloader
private enum ColumnFillMode { StretchLastColumn, Proportional }
private ColumnFillMode _fillMode = ColumnFillMode.Proportional;
private bool MarqueeEnabled = true;
private bool MarqueeOnlyWhenFocused = false;
private int MarqueeStartDelayMs = 250;
private int MarqueePauseMs = 500;
private float MarqueeSpeedPxPerSecond = 30f;
private int MarqueeFadeWidthPx = 8;
private int MarqueeOvershootPx = 2;
private int MinOverflowForMarqueePx = 2;
private float MarqueeMinProgressPerSecond = 0.15f;
@@ -342,7 +339,38 @@ namespace AndroidSideloader
private void OnScrollDetected()
{
_listView.Invalidate();
if (!_listView.IsHandleCreated)
return;
// Keep hover state in sync after scroll without forcing a full repaint
UpdateHoverFromCursor();
}
private void UpdateHoverFromCursor()
{
if (!_listView.IsHandleCreated)
return;
Point clientPt = _listView.PointToClient(Control.MousePosition);
int newHoveredIndex = -1;
if (_listView.ClientRectangle.Contains(clientPt) && !IsPointInHeader(clientPt))
{
var hit = _listView.HitTest(clientPt);
newHoveredIndex = hit.Item != null ? hit.Item.Index : -1;
}
if (newHoveredIndex == _hoveredItemIndex)
return;
int oldIndex = _hoveredItemIndex;
_hoveredItemIndex = newHoveredIndex;
if (oldIndex >= 0 && oldIndex < _listView.Items.Count)
_listView.RedrawItems(oldIndex, oldIndex, true);
if (newHoveredIndex >= 0 && newHoveredIndex < _listView.Items.Count)
_listView.RedrawItems(newHoveredIndex, newHoveredIndex, true);
}
private void OnHandleCreated(object sender, EventArgs e)
@@ -553,13 +581,6 @@ namespace AndroidSideloader
return (itemIndex % 2 == 1) ? RowAlt : RowNormal;
}
private int GetEffectiveOvershootPx()
{
int o = Math.Max(0, MarqueeOvershootPx);
o = Math.Max(o, Math.Max(0, MarqueeFadeWidthPx));
return o;
}
private void PaintHeaderRightGap(IntPtr headerHandle)
{
if (headerHandle == IntPtr.Zero || !_listView.IsHandleCreated) return;
@@ -741,12 +762,8 @@ namespace AndroidSideloader
private bool ShouldDrawMarquee(int itemIndex, int columnIndex, bool isSelected, Rectangle textBounds, string text)
{
if (!MarqueeEnabled) return false;
if (!isSelected) return false;
if (MarqueeOnlyWhenFocused && !_listView.Focused)
return false;
if (itemIndex != _marqueeSelectedIndex) return false;
if (string.IsNullOrEmpty(text)) return false;
if (textBounds.Width <= 8) return false;
@@ -985,8 +1002,6 @@ namespace AndroidSideloader
float[] oldMax = (float[])_marqueeMax.Clone();
ComputeMarqueeMaxForSelectedRow();
int overshoot = GetEffectiveOvershootPx();
for (int i = 0; i < _marqueeOffsets.Length; i++)
{
if (_marqueeMax[i] <= 0f)
@@ -999,10 +1014,10 @@ namespace AndroidSideloader
}
float overflow = _marqueeMax[i];
float travel = overflow + (2f * overshoot);
float travel = overflow + (2f * MarqueeFadeWidthPx);
float p = Clamp01(_marqueeProgress[i]);
_marqueeProgress[i] = p;
_marqueeOffsets[i] = (Ease(p) * travel) - overshoot;
_marqueeOffsets[i] = (Ease(p) * travel) - MarqueeFadeWidthPx;
}
UpdateMarqueeTimerState();
@@ -1061,18 +1076,6 @@ namespace AndroidSideloader
private void UpdateMarqueeTimerState()
{
if (!MarqueeEnabled)
{
if (_marqueeTimer.Enabled) _marqueeTimer.Stop();
return;
}
if (MarqueeOnlyWhenFocused && !_listView.Focused)
{
if (_marqueeTimer.Enabled) _marqueeTimer.Stop();
return;
}
bool any = false;
for (int i = 0; i < _marqueeMax.Length; i++)
{
@@ -1096,9 +1099,6 @@ namespace AndroidSideloader
private void UpdateMarquee()
{
if (!MarqueeEnabled) { _marqueeTimer.Stop(); return; }
if (MarqueeOnlyWhenFocused && !_listView.Focused) { _marqueeTimer.Stop(); return; }
var active = GetActiveSelectedItem();
int idx = active != null ? active.Index : -1;
if (idx != _marqueeSelectedIndex)
@@ -1127,8 +1127,6 @@ namespace AndroidSideloader
int pauseMs = Math.Max(0, MarqueePauseMs);
float speed = Math.Max(1f, MarqueeSpeedPxPerSecond);
int overshoot = GetEffectiveOvershootPx();
for (int col = 0; col < _marqueeOffsets.Length; col++)
{
float overflow = _marqueeMax[col];
@@ -1155,7 +1153,7 @@ namespace AndroidSideloader
int dir = _marqueeDirs[col];
if (dir == 0) dir = 1;
float travel = overflow + (2f * overshoot);
float travel = overflow + (2f * MarqueeFadeWidthPx);
float pSpeed = speed / (1.5f * travel);
pSpeed = Math.Max(pSpeed, MarqueeMinProgressPerSecond);
@@ -1175,7 +1173,7 @@ namespace AndroidSideloader
_marqueeHoldMs[col] = pauseMs;
}
float newOffset = (Ease(p) * travel) - overshoot;
float newOffset = (Ease(p) * travel) - MarqueeFadeWidthPx;
if (Math.Abs(newOffset - _marqueeOffsets[col]) > 0.02f || Math.Abs(p - _marqueeProgress[col]) > 0.0005f)
{