feat: Improve macOS app chooser functionality and enhance drag operation success criteria
Some checks are pending
Tests & Quality Checks / Test on Python 3.11 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.10 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-2 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-2 (push) Waiting to run
Tests & Quality Checks / Build Artifacts (push) Blocked by required conditions
Tests & Quality Checks / Build Artifacts-1 (push) Blocked by required conditions
Some checks are pending
Tests & Quality Checks / Test on Python 3.11 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-1 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.10 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.11-2 (push) Waiting to run
Tests & Quality Checks / Test on Python 3.12-2 (push) Waiting to run
Tests & Quality Checks / Build Artifacts (push) Blocked by required conditions
Tests & Quality Checks / Build Artifacts-1 (push) Blocked by required conditions
This commit is contained in:
parent
cbd8ed0186
commit
d6f4140947
3 changed files with 80 additions and 20 deletions
|
|
@ -146,7 +146,7 @@ class DragInterceptor(QWidget):
|
||||||
file_paths: Single local file path or list of local file paths
|
file_paths: Single local file path or list of local file paths
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
True if drag was created successfully
|
True if drag was initiated successfully
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Normalize to list
|
# Normalize to list
|
||||||
|
|
@ -170,10 +170,15 @@ class DragInterceptor(QWidget):
|
||||||
# drag.setPixmap(...)
|
# drag.setPixmap(...)
|
||||||
|
|
||||||
# Start drag operation (blocks until drop or cancel)
|
# Start drag operation (blocks until drop or cancel)
|
||||||
# Qt.CopyAction allows copying files
|
# In RDP/remote environments, drag.exec() may not return the expected result
|
||||||
|
# even though the drag was successful. We accept the drag as successful if
|
||||||
|
# it completes without exception.
|
||||||
result = drag.exec(Qt.DropAction.CopyAction)
|
result = drag.exec(Qt.DropAction.CopyAction)
|
||||||
|
|
||||||
return result == Qt.DropAction.CopyAction
|
# Accept any drop action result (including NoDropAction) as successful
|
||||||
|
# in remote environments like RDP where OS feedback is unreliable.
|
||||||
|
logger.debug(f"Drag operation completed with result: {result}")
|
||||||
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(f"Error creating native drag: {e}")
|
logger.exception(f"Error creating native drag: {e}")
|
||||||
|
|
|
||||||
|
|
@ -1185,20 +1185,55 @@ class MainWindow(QMainWindow):
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
if sys.platform == "darwin":
|
||||||
# Prompt for an app and open the file with the selected app.
|
# Prompt for an app and open the file with the selected app.
|
||||||
script = (
|
# Use AppleScript with choose application and open the file.
|
||||||
"on run argv\n"
|
# This more reliably opens files with chosen applications.
|
||||||
"set targetFile to POSIX file (item 1 of argv)\n"
|
# Use a simple, more direct approach
|
||||||
"set chosenApp to choose application\n"
|
# Get the chosen app via AppleScript, then use open command
|
||||||
'tell application "Finder" to open targetFile using chosenApp\n'
|
get_app_script = '''choose application with title "Select an application to open the file"'''
|
||||||
"end run"
|
try:
|
||||||
)
|
# Get the chosen application
|
||||||
result = subprocess.run(
|
app_result = subprocess.run(
|
||||||
["osascript", "-e", script, file_path],
|
["osascript", "-e", get_app_script],
|
||||||
check=False,
|
check=False,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
)
|
timeout=30,
|
||||||
return result.returncode == 0
|
)
|
||||||
|
|
||||||
|
if app_result.returncode != 0:
|
||||||
|
logger.warning(f"User cancelled app chooser or error occurred: {app_result.stderr}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Get the application name (strip whitespace)
|
||||||
|
chosen_app = app_result.stdout.strip()
|
||||||
|
if not chosen_app:
|
||||||
|
logger.warning("No application was selected")
|
||||||
|
return False
|
||||||
|
|
||||||
|
logger.info(f"User selected app: {chosen_app}")
|
||||||
|
|
||||||
|
# Now open the file with the chosen app using the 'open' command
|
||||||
|
open_result = subprocess.run(
|
||||||
|
["open", "-a", chosen_app, normalized_path],
|
||||||
|
check=False,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
|
||||||
|
if open_result.returncode == 0:
|
||||||
|
logger.info(f"Opened '{normalized_path}' with '{chosen_app}'")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
logger.warning(f"Failed to open file with '{chosen_app}': {open_result.stderr}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
logger.warning("App chooser timed out")
|
||||||
|
return False
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error in macOS app chooser: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
logger.warning(f"Open-with chooser not implemented for platform: {sys.platform}")
|
logger.warning(f"Open-with chooser not implemented for platform: {sys.platform}")
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -206,11 +206,31 @@ class TestMainWindowOpenWith:
|
||||||
test_file = sample_config.allowed_roots[0] / "open_with_macos.txt"
|
test_file = sample_config.allowed_roots[0] / "open_with_macos.txt"
|
||||||
test_file.write_text("test")
|
test_file.write_text("test")
|
||||||
|
|
||||||
class _Result:
|
call_count = [0] # Use list to make it mutable in nested function
|
||||||
|
|
||||||
|
class _AppChooseResult:
|
||||||
returncode = 0
|
returncode = 0
|
||||||
|
stdout = "TextEdit" # Simulated chosen app name
|
||||||
|
|
||||||
|
class _OpenResult:
|
||||||
|
returncode = 0
|
||||||
|
stdout = ""
|
||||||
|
|
||||||
|
def mock_run(*args, **kwargs):
|
||||||
|
"""Mock subprocess.run with two different behaviors per call."""
|
||||||
|
call_count[0] += 1
|
||||||
|
# First call: osascript to choose application
|
||||||
|
if call_count[0] == 1:
|
||||||
|
return _AppChooseResult()
|
||||||
|
# Second call: open command to open the file
|
||||||
|
elif call_count[0] == 2:
|
||||||
|
return _OpenResult()
|
||||||
|
else:
|
||||||
|
raise AssertionError(f"Unexpected call #{call_count[0]} to subprocess.run")
|
||||||
|
|
||||||
|
|
||||||
with patch("webdrop_bridge.ui.main_window.sys.platform", "darwin"):
|
with patch("webdrop_bridge.ui.main_window.sys.platform", "darwin"):
|
||||||
with patch("webdrop_bridge.ui.main_window.subprocess.run", return_value=_Result()):
|
with patch("webdrop_bridge.ui.main_window.subprocess.run", side_effect=mock_run):
|
||||||
assert window._open_with_app_chooser(str(test_file)) is True
|
assert window._open_with_app_chooser(str(test_file)) is True
|
||||||
|
|
||||||
def test_open_with_app_chooser_unsupported_platform(self, qtbot, sample_config):
|
def test_open_with_app_chooser_unsupported_platform(self, qtbot, sample_config):
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue