mirror of
https://github.com/Flow-Launcher/Flow.Launcher.git
synced 2026-03-11 08:54:32 +00:00
Merge branch 'dev' into ChannelSelect
This commit is contained in:
commit
1dd04475c5
428 changed files with 14134 additions and 5008 deletions
2
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
2
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
|
|
@ -16,6 +16,8 @@ body:
|
|||
I have checked that this issue has not already been reported.
|
||||
- label: >
|
||||
I am using the latest version of Flow Launcher.
|
||||
- label: >
|
||||
I am using the prerelease version of Flow Launcher.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
|
|
|||
242
.github/update_release_pr.py
vendored
Normal file
242
.github/update_release_pr.py
vendored
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
from os import getenv
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
def get_github_prs(token: str, owner: str, repo: str, label: str = "", state: str = "all") -> list[dict]:
|
||||
"""
|
||||
Fetches pull requests from a GitHub repository that match a given milestone and label.
|
||||
|
||||
Args:
|
||||
token (str): GitHub token.
|
||||
owner (str): The owner of the repository.
|
||||
repo (str): The name of the repository.
|
||||
label (str): The label name. Filter is not applied when empty string.
|
||||
state (str): State of PR, e.g. open, closed, all
|
||||
|
||||
Returns:
|
||||
list: A list of dictionaries, where each dictionary represents a pull request.
|
||||
Returns an empty list if no PRs are found or an error occurs.
|
||||
"""
|
||||
headers = {
|
||||
"Authorization": f"token {token}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
}
|
||||
|
||||
milestone_id = None
|
||||
milestone_url = f"https://api.github.com/repos/{owner}/{repo}/milestones"
|
||||
params = {"state": "open"}
|
||||
|
||||
try:
|
||||
response = requests.get(milestone_url, headers=headers, params=params)
|
||||
response.raise_for_status()
|
||||
milestones = response.json()
|
||||
|
||||
if len(milestones) > 2:
|
||||
print("More than two milestones found, unable to determine the milestone required.")
|
||||
exit(1)
|
||||
|
||||
# milestones.pop()
|
||||
for ms in milestones:
|
||||
if ms["title"] != "Future":
|
||||
milestone_id = ms["number"]
|
||||
print(f"Gathering PRs with milestone {ms['title']}...")
|
||||
break
|
||||
|
||||
if not milestone_id:
|
||||
print(f"No suitable milestone found in repository '{owner}/{repo}'.")
|
||||
exit(1)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error fetching milestones: {e}")
|
||||
exit(1)
|
||||
|
||||
# This endpoint allows filtering by milestone and label. A PR in GH's perspective is a type of issue.
|
||||
prs_url = f"https://api.github.com/repos/{owner}/{repo}/issues"
|
||||
params = {
|
||||
"state": state,
|
||||
"milestone": milestone_id,
|
||||
"labels": label,
|
||||
"per_page": 100,
|
||||
}
|
||||
|
||||
all_prs = []
|
||||
page = 1
|
||||
while True:
|
||||
try:
|
||||
params["page"] = page
|
||||
response = requests.get(prs_url, headers=headers, params=params)
|
||||
response.raise_for_status() # Raise an exception for HTTP errors
|
||||
prs = response.json()
|
||||
|
||||
if not prs:
|
||||
break # No more PRs to fetch
|
||||
|
||||
# Check for pr key since we are using issues endpoint instead.
|
||||
all_prs.extend([item for item in prs if "pull_request" in item])
|
||||
page += 1
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error fetching pull requests: {e}")
|
||||
exit(1)
|
||||
|
||||
return all_prs
|
||||
|
||||
|
||||
def get_prs(pull_request_items: list[dict], label: str = "", state: str = "all") -> list[dict]:
|
||||
"""
|
||||
Returns a list of pull requests after applying the label and state filters.
|
||||
|
||||
Args:
|
||||
pull_request_items (list[dict]): List of PR items.
|
||||
label (str): The label name. Filter is not applied when empty string.
|
||||
state (str): State of PR, e.g. open, closed, all
|
||||
|
||||
Returns:
|
||||
list: A list of dictionaries, where each dictionary represents a pull request.
|
||||
Returns an empty list if no PRs are found.
|
||||
"""
|
||||
pr_list = []
|
||||
count = 0
|
||||
for pr in pull_request_items:
|
||||
if state in [pr["state"], "all"] and (not label or [item for item in pr["labels"] if item["name"] == label]):
|
||||
pr_list.append(pr)
|
||||
count += 1
|
||||
|
||||
print(f"Found {count} PRs with {label if label else 'no filter on'} label and state as {state}")
|
||||
|
||||
return pr_list
|
||||
|
||||
def get_prs_assignees(pull_request_items: list[dict], label: str = "", state: str = "all") -> list[str]:
|
||||
"""
|
||||
Returns a list of pull request assignees after applying the label and state filters, excludes jjw24.
|
||||
|
||||
Args:
|
||||
pull_request_items (list[dict]): List of PR items.
|
||||
label (str): The label name. Filter is not applied when empty string.
|
||||
state (str): State of PR, e.g. open, closed, all
|
||||
|
||||
Returns:
|
||||
list: A list of strs, where each string is an assignee name. List is not distinct, so can contain
|
||||
duplicate names.
|
||||
Returns an empty list if none are found.
|
||||
"""
|
||||
assignee_list = []
|
||||
for pr in pull_request_items:
|
||||
if state in [pr["state"], "all"] and (not label or [item for item in pr["labels"] if item["name"] == label]):
|
||||
[assignee_list.append(assignee["login"]) for assignee in pr["assignees"] if assignee["login"] != "jjw24" ]
|
||||
|
||||
print(f"Found {len(assignee_list)} assignees with {label if label else 'no filter on'} label and state as {state}")
|
||||
|
||||
return assignee_list
|
||||
|
||||
def get_pr_descriptions(pull_request_items: list[dict]) -> str:
|
||||
"""
|
||||
Returns the concatenated string of pr title and number in the format of
|
||||
'- PR title 1 #3651
|
||||
- PR title 2 #3652
|
||||
- PR title 3 #3653
|
||||
'
|
||||
|
||||
Args:
|
||||
pull_request_items (list[dict]): List of PR items.
|
||||
|
||||
Returns:
|
||||
str: a string of PR titles and numbers
|
||||
"""
|
||||
description_content = ""
|
||||
for pr in pull_request_items:
|
||||
description_content += f"- {pr['title']} #{pr['number']}\n"
|
||||
|
||||
return description_content
|
||||
|
||||
|
||||
def update_pull_request_description(token: str, owner: str, repo: str, pr_number: int, new_description: str) -> None:
|
||||
"""
|
||||
Updates the description (body) of a GitHub Pull Request.
|
||||
|
||||
Args:
|
||||
token (str): Token.
|
||||
owner (str): The owner of the repository.
|
||||
repo (str): The name of the repository.
|
||||
pr_number (int): The number of the pull request to update.
|
||||
new_description (str): The new content for the PR's description.
|
||||
|
||||
Returns:
|
||||
dict or None: The updated PR object (as a dictionary) if successful,
|
||||
None otherwise.
|
||||
"""
|
||||
headers = {
|
||||
"Authorization": f"token {token}",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pr_number}"
|
||||
|
||||
payload = {"body": new_description}
|
||||
|
||||
print(f"Attempting to update PR #{pr_number} in {owner}/{repo}...")
|
||||
print(f"URL: {url}")
|
||||
|
||||
try:
|
||||
response = None
|
||||
response = requests.patch(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
|
||||
print(f"Successfully updated PR #{pr_number}.")
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"Error updating pull request #{pr_number}: {e}")
|
||||
if response is not None:
|
||||
print(f"Response status code: {response.status_code}")
|
||||
print(f"Response text: {response.text}")
|
||||
exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
github_token = getenv("GITHUB_TOKEN")
|
||||
|
||||
if not github_token:
|
||||
print("Error: GITHUB_TOKEN environment variable not set.")
|
||||
exit(1)
|
||||
|
||||
repository_owner = "flow-launcher"
|
||||
repository_name = "flow.launcher"
|
||||
state = "all"
|
||||
|
||||
print(f"Fetching {state} PRs for {repository_owner}/{repository_name} ...")
|
||||
|
||||
pull_requests = get_github_prs(github_token, repository_owner, repository_name)
|
||||
|
||||
if not pull_requests:
|
||||
print("No matching pull requests found")
|
||||
exit(1)
|
||||
|
||||
print(f"\nFound total of {len(pull_requests)} pull requests")
|
||||
|
||||
release_pr = get_prs(pull_requests, "release", "open")
|
||||
|
||||
if len(release_pr) != 1:
|
||||
print(f"Unable to find the exact release PR. Returned result: {release_pr}")
|
||||
exit(1)
|
||||
|
||||
print(f"Found release PR: {release_pr[0]['title']}")
|
||||
|
||||
enhancement_prs = get_prs(pull_requests, "enhancement", "closed")
|
||||
bug_fix_prs = get_prs(pull_requests, "bug", "closed")
|
||||
|
||||
description_content = "# Release notes\n"
|
||||
description_content += f"## Features\n{get_pr_descriptions(enhancement_prs)}" if enhancement_prs else ""
|
||||
description_content += f"## Bug fixes\n{get_pr_descriptions(bug_fix_prs)}" if bug_fix_prs else ""
|
||||
|
||||
assignees = list(set(get_prs_assignees(pull_requests, "enhancement", "closed") + get_prs_assignees(pull_requests, "bug", "closed")))
|
||||
assignees.sort(key=str.lower)
|
||||
|
||||
description_content += f"### Authors:\n{', '.join(assignees)}"
|
||||
|
||||
update_pull_request_description(
|
||||
github_token, repository_owner, repository_name, release_pr[0]["number"], description_content
|
||||
)
|
||||
|
||||
print(f"PR content updated to:\n{description_content}")
|
||||
85
.github/workflows/default_plugins.yml
vendored
85
.github/workflows/default_plugins.yml
vendored
|
|
@ -3,11 +3,10 @@ name: Publish Default Plugins
|
|||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
paths: ['Plugins/**']
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
publish:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
|
|
@ -17,39 +16,24 @@ jobs:
|
|||
with:
|
||||
dotnet-version: 7.0.x
|
||||
|
||||
- name: Determine New Plugin Updates
|
||||
uses: dorny/paths-filter@v3
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
browserbookmark:
|
||||
- 'Plugins/Flow.Launcher.Plugin.BrowserBookmark/plugin.json'
|
||||
calculator:
|
||||
- 'Plugins/Flow.Launcher.Plugin.Calculator/plugin.json'
|
||||
explorer:
|
||||
- 'Plugins/Flow.Launcher.Plugin.Explorer/plugin.json'
|
||||
pluginindicator:
|
||||
- 'Plugins/Flow.Launcher.Plugin.PluginIndicator/plugin.json'
|
||||
pluginsmanager:
|
||||
- 'Plugins/Flow.Launcher.Plugin.PluginsManager/plugin.json'
|
||||
processkiller:
|
||||
- 'Plugins/Flow.Launcher.Plugin.ProcessKiller/plugin.json'
|
||||
program:
|
||||
- 'Plugins/Flow.Launcher.Plugin.Program/plugin.json'
|
||||
shell:
|
||||
- 'Plugins/Flow.Launcher.Plugin.Shell/plugin.json'
|
||||
sys:
|
||||
- 'Plugins/Flow.Launcher.Plugin.Sys/plugin.json'
|
||||
url:
|
||||
- 'Plugins/Flow.Launcher.Plugin.Url/plugin.json'
|
||||
websearch:
|
||||
- 'Plugins/Flow.Launcher.Plugin.WebSearch/plugin.json'
|
||||
windowssettings:
|
||||
- 'Plugins/Flow.Launcher.Plugin.WindowsSettings/plugin.json'
|
||||
base: 'master'
|
||||
- name: Update Plugins To Production Version
|
||||
run: |
|
||||
$version = "1.0.0"
|
||||
Get-Content appveyor.yml | ForEach-Object {
|
||||
if ($_ -match "version:\s*'(\d+\.\d+\.\d+)\.") {
|
||||
$version = $matches[1]
|
||||
}
|
||||
}
|
||||
|
||||
$jsonFiles = Get-ChildItem -Path ".\Plugins\*\plugin.json"
|
||||
foreach ($file in $jsonFiles) {
|
||||
$plugin_old_ver = Get-Content $file.FullName -Raw | ConvertFrom-Json
|
||||
(Get-Content $file) -replace '"Version"\s*:\s*".*?"', "`"Version`": `"$version`"" | Set-Content $file
|
||||
$plugin_new_ver = Get-Content $file.FullName -Raw | ConvertFrom-Json
|
||||
Write-Host "Updated" $plugin_old_ver.Name "version from" $plugin_old_ver.Version "to" $plugin_new_ver.Version
|
||||
}
|
||||
|
||||
- name: Get BrowserBookmark Version
|
||||
if: steps.changes.outputs.browserbookmark == 'true'
|
||||
id: updated-version-browserbookmark
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -57,14 +41,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build BrowserBookmark
|
||||
if: steps.changes.outputs.browserbookmark == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.BrowserBookmark"
|
||||
7z a -tzip "Flow.Launcher.Plugin.BrowserBookmark.zip" "./Flow.Launcher.Plugin.BrowserBookmark/*"
|
||||
rm -r "Flow.Launcher.Plugin.BrowserBookmark"
|
||||
|
||||
- name: Publish BrowserBookmark
|
||||
if: steps.changes.outputs.browserbookmark == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.BrowserBookmark"
|
||||
|
|
@ -76,7 +58,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get Calculator Version
|
||||
if: steps.changes.outputs.calculator == 'true'
|
||||
id: updated-version-calculator
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -84,14 +65,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build Calculator
|
||||
if: steps.changes.outputs.calculator == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.Calculator/Flow.Launcher.Plugin.Calculator.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Calculator"
|
||||
7z a -tzip "Flow.Launcher.Plugin.Calculator.zip" "./Flow.Launcher.Plugin.Calculator/*"
|
||||
rm -r "Flow.Launcher.Plugin.Calculator"
|
||||
|
||||
- name: Publish Calculator
|
||||
if: steps.changes.outputs.calculator == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.Calculator"
|
||||
|
|
@ -103,7 +82,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get Explorer Version
|
||||
if: steps.changes.outputs.explorer == 'true'
|
||||
id: updated-version-explorer
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -111,14 +89,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build Explorer
|
||||
if: steps.changes.outputs.explorer == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.Explorer/Flow.Launcher.Plugin.Explorer.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Explorer"
|
||||
7z a -tzip "Flow.Launcher.Plugin.Explorer.zip" "./Flow.Launcher.Plugin.Explorer/*"
|
||||
rm -r "Flow.Launcher.Plugin.Explorer"
|
||||
|
||||
- name: Publish Explorer
|
||||
if: steps.changes.outputs.explorer == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.Explorer"
|
||||
|
|
@ -130,7 +106,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get PluginIndicator Version
|
||||
if: steps.changes.outputs.pluginindicator == 'true'
|
||||
id: updated-version-pluginindicator
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -138,14 +113,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build PluginIndicator
|
||||
if: steps.changes.outputs.pluginindicator == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.PluginIndicator/Flow.Launcher.Plugin.PluginIndicator.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.PluginIndicator"
|
||||
7z a -tzip "Flow.Launcher.Plugin.PluginIndicator.zip" "./Flow.Launcher.Plugin.PluginIndicator/*"
|
||||
rm -r "Flow.Launcher.Plugin.PluginIndicator"
|
||||
|
||||
- name: Publish PluginIndicator
|
||||
if: steps.changes.outputs.pluginindicator == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginIndicator"
|
||||
|
|
@ -157,7 +130,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get PluginsManager Version
|
||||
if: steps.changes.outputs.pluginsmanager == 'true'
|
||||
id: updated-version-pluginsmanager
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -165,14 +137,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build PluginsManager
|
||||
if: steps.changes.outputs.pluginsmanager == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.PluginsManager/Flow.Launcher.Plugin.PluginsManager.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.PluginsManager"
|
||||
7z a -tzip "Flow.Launcher.Plugin.PluginsManager.zip" "./Flow.Launcher.Plugin.PluginsManager/*"
|
||||
rm -r "Flow.Launcher.Plugin.PluginsManager"
|
||||
|
||||
- name: Publish PluginsManager
|
||||
if: steps.changes.outputs.pluginsmanager == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginsManager"
|
||||
|
|
@ -184,7 +154,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get ProcessKiller Version
|
||||
if: steps.changes.outputs.processkiller == 'true'
|
||||
id: updated-version-processkiller
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -192,14 +161,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build ProcessKiller
|
||||
if: steps.changes.outputs.processkiller == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.ProcessKiller/Flow.Launcher.Plugin.ProcessKiller.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.ProcessKiller"
|
||||
7z a -tzip "Flow.Launcher.Plugin.ProcessKiller.zip" "./Flow.Launcher.Plugin.ProcessKiller/*"
|
||||
rm -r "Flow.Launcher.Plugin.ProcessKiller"
|
||||
|
||||
- name: Publish ProcessKiller
|
||||
if: steps.changes.outputs.processkiller == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller"
|
||||
|
|
@ -211,7 +178,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get Program Version
|
||||
if: steps.changes.outputs.program == 'true'
|
||||
id: updated-version-program
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -219,14 +185,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build Program
|
||||
if: steps.changes.outputs.program == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.Program/Flow.Launcher.Plugin.Program.csproj' --framework net7.0-windows10.0.19041.0 -c Release -o "Flow.Launcher.Plugin.Program"
|
||||
7z a -tzip "Flow.Launcher.Plugin.Program.zip" "./Flow.Launcher.Plugin.Program/*"
|
||||
rm -r "Flow.Launcher.Plugin.Program"
|
||||
|
||||
- name: Publish Program
|
||||
if: steps.changes.outputs.program == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.Program"
|
||||
|
|
@ -238,7 +202,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get Shell Version
|
||||
if: steps.changes.outputs.shell == 'true'
|
||||
id: updated-version-shell
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -246,14 +209,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build Shell
|
||||
if: steps.changes.outputs.shell == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.Shell/Flow.Launcher.Plugin.Shell.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Shell"
|
||||
7z a -tzip "Flow.Launcher.Plugin.Shell.zip" "./Flow.Launcher.Plugin.Shell/*"
|
||||
rm -r "Flow.Launcher.Plugin.Shell"
|
||||
|
||||
- name: Publish Shell
|
||||
if: steps.changes.outputs.shell == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.Shell"
|
||||
|
|
@ -265,7 +226,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get Sys Version
|
||||
if: steps.changes.outputs.sys == 'true'
|
||||
id: updated-version-sys
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -273,14 +233,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build Sys
|
||||
if: steps.changes.outputs.sys == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Sys"
|
||||
7z a -tzip "Flow.Launcher.Plugin.Sys.zip" "./Flow.Launcher.Plugin.Sys/*"
|
||||
rm -r "Flow.Launcher.Plugin.Sys"
|
||||
|
||||
- name: Publish Sys
|
||||
if: steps.changes.outputs.sys == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.Sys"
|
||||
|
|
@ -292,7 +250,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get Url Version
|
||||
if: steps.changes.outputs.url == 'true'
|
||||
id: updated-version-url
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -300,14 +257,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build Url
|
||||
if: steps.changes.outputs.url == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.Url/Flow.Launcher.Plugin.Url.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.Url"
|
||||
7z a -tzip "Flow.Launcher.Plugin.Url.zip" "./Flow.Launcher.Plugin.Url/*"
|
||||
rm -r "Flow.Launcher.Plugin.Url"
|
||||
|
||||
- name: Publish Url
|
||||
if: steps.changes.outputs.url == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.Url"
|
||||
|
|
@ -319,7 +274,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get WebSearch Version
|
||||
if: steps.changes.outputs.websearch == 'true'
|
||||
id: updated-version-websearch
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -327,14 +281,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build WebSearch
|
||||
if: steps.changes.outputs.websearch == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.WebSearch/Flow.Launcher.Plugin.WebSearch.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.WebSearch"
|
||||
7z a -tzip "Flow.Launcher.Plugin.WebSearch.zip" "./Flow.Launcher.Plugin.WebSearch/*"
|
||||
rm -r "Flow.Launcher.Plugin.WebSearch"
|
||||
|
||||
- name: Publish WebSearch
|
||||
if: steps.changes.outputs.websearch == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.WebSearch"
|
||||
|
|
@ -346,7 +298,6 @@ jobs:
|
|||
|
||||
|
||||
- name: Get WindowsSettings Version
|
||||
if: steps.changes.outputs.windowssettings == 'true'
|
||||
id: updated-version-windowssettings
|
||||
uses: notiz-dev/github-action-json-property@release
|
||||
with:
|
||||
|
|
@ -354,14 +305,12 @@ jobs:
|
|||
prop_path: 'Version'
|
||||
|
||||
- name: Build WindowsSettings
|
||||
if: steps.changes.outputs.windowssettings == 'true'
|
||||
run: |
|
||||
dotnet publish 'Plugins/Flow.Launcher.Plugin.WindowsSettings/Flow.Launcher.Plugin.WindowsSettings.csproj' --framework net7.0-windows -c Release -o "Flow.Launcher.Plugin.WindowsSettings"
|
||||
7z a -tzip "Flow.Launcher.Plugin.WindowsSettings.zip" "./Flow.Launcher.Plugin.WindowsSettings/*"
|
||||
rm -r "Flow.Launcher.Plugin.WindowsSettings"
|
||||
|
||||
- name: Publish WindowsSettings
|
||||
if: steps.changes.outputs.windowssettings == 'true'
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
repository: "Flow-Launcher/Flow.Launcher.Plugin.WindowsSettings"
|
||||
|
|
|
|||
91
.github/workflows/dotnet.yml
vendored
Normal file
91
.github/workflows/dotnet.yml
vendored
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
# This workflow will build a .NET project
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net
|
||||
|
||||
name: Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
FlowVersion: 1.19.5
|
||||
NUGET_CERT_REVOCATION_MODE: offline
|
||||
BUILD_NUMBER: ${{ github.run_number }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set Flow.Launcher.csproj version
|
||||
id: update
|
||||
uses: vers-one/dotnet-project-version-updater@v1.7
|
||||
with:
|
||||
file: |
|
||||
"**/SolutionAssemblyInfo.cs"
|
||||
version: ${{ env.FlowVersion }}.${{ env.BUILD_NUMBER }}
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
# cache: true
|
||||
# cache-dependency-path: |
|
||||
# Flow.Launcher/packages.lock.json
|
||||
# Flow.Launcher.Core/packages.lock.json
|
||||
# Flow.Launcher.Infrastructure/packages.lock.json
|
||||
# Flow.Launcher.Plugin/packages.lock.json
|
||||
- name: Install vpk
|
||||
run: dotnet tool install -g vpk
|
||||
- name: Restore dependencies
|
||||
run: nuget restore
|
||||
- name: Build
|
||||
run: dotnet build --no-restore -c Release
|
||||
- name: Initialize Service
|
||||
run: |
|
||||
sc config WSearch start= auto # Starts Windows Search service- Needed for running ExplorerTest
|
||||
net start WSearch
|
||||
- name: Test
|
||||
run: dotnet test --no-build --verbosity normal -c Release
|
||||
- name: Perform post_build tasks
|
||||
shell: powershell
|
||||
run: .\Scripts\post_build.ps1
|
||||
- name: Upload Plugin Nupkg
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Plugin nupkg
|
||||
path: |
|
||||
Output\Release\Flow.Launcher.Plugin.*.nupkg
|
||||
compression-level: 0
|
||||
- name: Upload Setup
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Flow Installer
|
||||
path: |
|
||||
Output\Packages\Flow-Launcher-*.exe
|
||||
compression-level: 0
|
||||
- name: Upload Portable Version
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Portable Version
|
||||
path: |
|
||||
Output\Packages\Flow-Launcher-Portable.zip
|
||||
compression-level: 0
|
||||
- name: Upload Full Nupkg
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Full nupkg
|
||||
path: |
|
||||
Output\Packages\FlowLauncher-*-full.nupkg
|
||||
|
||||
compression-level: 0
|
||||
- name: Upload Release Information
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: RELEASES
|
||||
path: |
|
||||
Output\Packages\RELEASES
|
||||
compression-level: 0
|
||||
34
.github/workflows/release_deploy.yml
vendored
Normal file
34
.github/workflows/release_deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
|
||||
name: New Release Deployments
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy-website:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger dispatch event for deploying website
|
||||
run: |
|
||||
http_status=$(curl -L -f -s -o /dev/null -w "%{http_code}" \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.DEPLOY_FLOW_WEBSITE }}" \
|
||||
https://api.github.com/repos/Flow-Launcher/flow-launcher.github.io/dispatches \
|
||||
-d '{"event_type":"deploy"}')
|
||||
if [ "$http_status" -ne 204 ]; then echo "Error: Deploy website failed, HTTP status code is $http_status"; exit 1; fi
|
||||
|
||||
publish-chocolatey:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Trigger dispatch event for publishing to Chocolatey
|
||||
run: |
|
||||
http_status=$(curl -L -f -s -o /dev/null -w "%{http_code}" \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.Publish_Chocolatey }}" \
|
||||
https://api.github.com/repos/Flow-Launcher/chocolatey-package/dispatches \
|
||||
-d '{"event_type":"publish"}')
|
||||
if [ "$http_status" -ne 204 ]; then echo "Error: Publish Chocolatey package failed, HTTP status code is $http_status"; exit 1; fi
|
||||
25
.github/workflows/release_pr.yml
vendored
Normal file
25
.github/workflows/release_pr.yml
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
name: Update release PR
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Run release PR update
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.PR_TOKEN }}
|
||||
run: |
|
||||
pip install requests -q
|
||||
python3 ./.github/update_release_pr.py
|
||||
5
.github/workflows/spelling.yml
vendored
5
.github/workflows/spelling.yml
vendored
|
|
@ -41,9 +41,8 @@ on:
|
|||
# tags-ignore:
|
||||
# - "**"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- '**'
|
||||
# - '!l10n_dev'
|
||||
branches-ignore:
|
||||
- master
|
||||
tags-ignore:
|
||||
- "**"
|
||||
types:
|
||||
|
|
|
|||
|
|
@ -1,21 +1,22 @@
|
|||
using Microsoft.Win32;
|
||||
using Squirrel;
|
||||
using System;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System.Linq;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Microsoft.Win32;
|
||||
using Squirrel;
|
||||
|
||||
namespace Flow.Launcher.Core.Configuration
|
||||
{
|
||||
public class Portable : IPortable
|
||||
{
|
||||
private static readonly string ClassName = nameof(Portable);
|
||||
|
||||
private readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -51,7 +52,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("|Portable.DisablePortableMode|Error occurred while disabling portable mode", e);
|
||||
API.LogException(ClassName, "Error occurred while disabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +76,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("|Portable.EnablePortableMode|Error occurred while enabling portable mode", e);
|
||||
API.LogException(ClassName, "Error occurred while enabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,32 @@
|
|||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Json;
|
||||
using System.Net.Sockets;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins
|
||||
{
|
||||
public record CommunityPluginSource(string ManifestFileUrl)
|
||||
{
|
||||
private static readonly string ClassName = nameof(CommunityPluginSource);
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
private string latestEtag = "";
|
||||
|
||||
private List<UserPlugin> plugins = new();
|
||||
|
||||
private static JsonSerializerOptions PluginStoreItemSerializationOption = new JsonSerializerOptions()
|
||||
private static readonly JsonSerializerOptions PluginStoreItemSerializationOption = new()
|
||||
{
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
|
||||
};
|
||||
|
|
@ -34,35 +41,49 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
/// </remarks>
|
||||
public async Task<List<UserPlugin>> FetchAsync(CancellationToken token)
|
||||
{
|
||||
Log.Info(nameof(CommunityPluginSource), $"Loading plugins from {ManifestFileUrl}");
|
||||
API.LogInfo(ClassName, $"Loading plugins from {ManifestFileUrl}");
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, ManifestFileUrl);
|
||||
|
||||
request.Headers.Add("If-None-Match", latestEtag);
|
||||
|
||||
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token)
|
||||
try
|
||||
{
|
||||
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
this.plugins = await response.Content
|
||||
.ReadFromJsonAsync<List<UserPlugin>>(PluginStoreItemSerializationOption, cancellationToken: token)
|
||||
.ConfigureAwait(false);
|
||||
this.latestEtag = response.Headers.ETag?.Tag;
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
plugins = await response.Content
|
||||
.ReadFromJsonAsync<List<UserPlugin>>(PluginStoreItemSerializationOption, cancellationToken: token)
|
||||
.ConfigureAwait(false);
|
||||
latestEtag = response.Headers.ETag?.Tag;
|
||||
|
||||
Log.Info(nameof(CommunityPluginSource), $"Loaded {this.plugins.Count} plugins from {ManifestFileUrl}");
|
||||
return this.plugins;
|
||||
API.LogInfo(ClassName, $"Loaded {plugins.Count} plugins from {ManifestFileUrl}");
|
||||
return plugins;
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.NotModified)
|
||||
{
|
||||
API.LogInfo(ClassName, $"Resource {ManifestFileUrl} has not been modified.");
|
||||
return plugins;
|
||||
}
|
||||
else
|
||||
{
|
||||
API.LogWarn(ClassName, $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (response.StatusCode == HttpStatusCode.NotModified)
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Info(nameof(CommunityPluginSource), $"Resource {ManifestFileUrl} has not been modified.");
|
||||
return this.plugins;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warn(nameof(CommunityPluginSource),
|
||||
$"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
|
||||
throw new Exception($"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}");
|
||||
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
|
||||
{
|
||||
API.LogException(ClassName, $"Check your connection and proxy settings to {ManifestFileUrl}.", e);
|
||||
}
|
||||
else
|
||||
{
|
||||
API.LogException(ClassName, "Error Occurred", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,10 +40,14 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
var completedTask = await Task.WhenAny(tasks);
|
||||
if (completedTask.IsCompletedSuccessfully)
|
||||
{
|
||||
// one of the requests completed successfully; keep its results
|
||||
// and cancel the remaining http requests.
|
||||
pluginResults = await completedTask;
|
||||
cts.Cancel();
|
||||
var result = await completedTask;
|
||||
if (result != null)
|
||||
{
|
||||
// one of the requests completed successfully; keep its results
|
||||
// and cancel the remaining http requests.
|
||||
pluginResults = result;
|
||||
cts.Cancel();
|
||||
}
|
||||
}
|
||||
tasks.Remove(completedTask);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System.Linq;
|
|||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
|
@ -14,6 +13,8 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
{
|
||||
public abstract class AbstractPluginEnvironment
|
||||
{
|
||||
private static readonly string ClassName = nameof(AbstractPluginEnvironment);
|
||||
|
||||
protected readonly IPublicAPI API = Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
internal abstract string Language { get; }
|
||||
|
|
@ -120,7 +121,7 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
else
|
||||
{
|
||||
API.ShowMsgBox(string.Format(API.GetTranslation("runtimePluginUnableToSetExecutablePath"), Language));
|
||||
Log.Error("PluginsLoader",
|
||||
API.LogError(ClassName,
|
||||
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
|
||||
$"{Language}Environment");
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Flow.Launcher.Plugin;
|
|||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
|
||||
internal class JavaScriptEnvironment : TypeScriptEnvironment
|
||||
{
|
||||
internal override string Language => AllowedLanguage.JavaScript;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ using Flow.Launcher.Plugin;
|
|||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
|
||||
internal class JavaScriptV2Environment : TypeScriptV2Environment
|
||||
{
|
||||
internal override string Language => AllowedLanguage.JavaScriptV2;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Flow.Launcher.Core.Plugin;
|
|||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
|
|
@ -30,13 +31,15 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||
|
||||
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
|
||||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
// Python 3.11.4 is no longer Windows 7 compatible. If user is on Win 7 and
|
||||
// uses Python plugin they need to custom install and use v3.8.9
|
||||
DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath).Wait();
|
||||
JTF.Run(() => DroplexPackage.Drop(App.python_3_11_4_embeddable, InstallPath));
|
||||
|
||||
PluginsSettingsFilePath = ExecutablePath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Flow.Launcher.Core.Plugin;
|
|||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
|
|
@ -27,11 +28,13 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||
|
||||
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
|
||||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
|
||||
JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath));
|
||||
|
||||
PluginsSettingsFilePath = ExecutablePath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Flow.Launcher.Core.Plugin;
|
|||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
|
|
@ -27,11 +28,13 @@ namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
|||
|
||||
internal TypeScriptV2Environment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||
|
||||
private JoinableTaskFactory JTF { get; } = new JoinableTaskFactory(new JoinableTaskContext());
|
||||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath, (s) => API.ShowMsgBox(s));
|
||||
|
||||
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
|
||||
JTF.Run(() => DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath));
|
||||
|
||||
PluginsSettingsFilePath = ExecutablePath;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
{
|
||||
public static class PluginsManifest
|
||||
{
|
||||
private static readonly string ClassName = nameof(PluginsManifest);
|
||||
|
||||
private static readonly CommunityPluginStore mainPluginStore =
|
||||
new("https://raw.githubusercontent.com/Flow-Launcher/Flow.Launcher.PluginsManifest/plugin_api_v2/plugins.json",
|
||||
"https://fastly.jsdelivr.net/gh/Flow-Launcher/Flow.Launcher.PluginsManifest@plugin_api_v2/plugins.json",
|
||||
|
|
@ -44,7 +46,7 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Ioc.Default.GetRequiredService<IPublicAPI>().LogException(nameof(PluginsManifest), "Http request failed", e);
|
||||
Ioc.Default.GetRequiredService<IPublicAPI>().LogException(ClassName, "Http request failed", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
|
@ -7,10 +6,9 @@ using System.Text;
|
|||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Microsoft.IO;
|
||||
using System.Windows;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -20,7 +18,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// </summary>
|
||||
internal abstract class JsonRPCPlugin : JsonRPCPluginBase
|
||||
{
|
||||
public const string JsonRPC = "JsonRPC";
|
||||
public new const string JsonRPC = "JsonRPC";
|
||||
|
||||
private static readonly string ClassName = nameof(JsonRPCPlugin);
|
||||
|
||||
protected abstract Task<Stream> RequestAsync(JsonRPCRequestModel rpcRequest, CancellationToken token = default);
|
||||
protected abstract string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default);
|
||||
|
|
@ -29,9 +29,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
private int RequestId { get; set; }
|
||||
|
||||
private string SettingConfigurationPath => Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
||||
private string SettingPath => Path.Combine(Context.CurrentPluginMetadata.PluginSettingsDirectoryPath, "Settings.json");
|
||||
|
||||
public override List<Result> LoadContextMenus(Result selectedResult)
|
||||
{
|
||||
var request = new JsonRPCRequestModel(RequestId++,
|
||||
|
|
@ -57,13 +54,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
};
|
||||
|
||||
private static readonly JsonSerializerOptions settingSerializeOption = new()
|
||||
{
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
private readonly Dictionary<string, FrameworkElement> _settingControls = new();
|
||||
|
||||
private async Task<List<Result>> DeserializedResultAsync(Stream output)
|
||||
{
|
||||
await using (output)
|
||||
|
|
@ -122,7 +112,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return !result.JsonRPCAction.DontHideAfterAction;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Execute external program and return the output
|
||||
/// </summary>
|
||||
|
|
@ -160,11 +149,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
var error = standardError.ReadToEnd();
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
Log.Error($"|JsonRPCPlugin.Execute|{error}");
|
||||
Context.API.LogError(ClassName, error);
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error.");
|
||||
Context.API.LogError(ClassName, "Empty standard output and standard error.");
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
|
|
@ -172,8 +161,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(
|
||||
$"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
|
||||
Context.API.LogException(ClassName,
|
||||
$"Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
|
||||
e);
|
||||
return string.Empty;
|
||||
}
|
||||
|
|
@ -184,7 +173,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
using var process = Process.Start(startInfo);
|
||||
if (process == null)
|
||||
{
|
||||
Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process");
|
||||
Context.API.LogError(ClassName, "Can't start new process");
|
||||
return Stream.Null;
|
||||
}
|
||||
|
||||
|
|
@ -204,7 +193,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("|JsonRPCPlugin.ExecuteAsync|Exception when kill process", e);
|
||||
Context.API.LogException(ClassName, "Exception when kill process", e);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -225,7 +214,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
case (0, 0):
|
||||
const string errorMessage = "Empty JSON-RPC Response.";
|
||||
Log.Warn($"|{nameof(JsonRPCPlugin)}.{nameof(ExecuteAsync)}|{errorMessage}");
|
||||
Context.API.LogWarn(ClassName, errorMessage);
|
||||
break;
|
||||
case (_, not 0):
|
||||
throw new InvalidDataException(Encoding.UTF8.GetString(errorBuffer.ToArray())); // The process has exited with an error message
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin;
|
||||
using YamlDotNet.Serialization;
|
||||
using YamlDotNet.Serialization.NamingConventions;
|
||||
|
|
@ -19,10 +19,9 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// </summary>
|
||||
public abstract class JsonRPCPluginBase : IAsyncPlugin, IContextMenu, ISettingProvider, ISavable
|
||||
{
|
||||
protected PluginInitContext Context;
|
||||
public const string JsonRPC = "JsonRPC";
|
||||
|
||||
private int RequestId { get; set; }
|
||||
protected PluginInitContext Context;
|
||||
|
||||
private string SettingConfigurationPath =>
|
||||
Path.Combine(Context.CurrentPluginMetadata.PluginDirectory, "SettingsTemplate.yaml");
|
||||
|
|
@ -107,7 +106,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public abstract Task<List<Result>> QueryAsync(Query query, CancellationToken token);
|
||||
|
||||
|
||||
private async Task InitSettingAsync()
|
||||
{
|
||||
JsonRpcConfigurationModel configuration = null;
|
||||
|
|
@ -119,7 +117,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
await File.ReadAllTextAsync(SettingConfigurationPath));
|
||||
}
|
||||
|
||||
|
||||
Settings ??= new JsonRPCPluginSettings
|
||||
{
|
||||
Configuration = configuration, SettingPath = SettingPath, API = Context.API
|
||||
|
|
@ -130,7 +127,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public virtual async Task InitAsync(PluginInitContext context)
|
||||
{
|
||||
this.Context = context;
|
||||
Context = context;
|
||||
await InitSettingAsync();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ using Flow.Launcher.Plugin;
|
|||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
public class JsonRPCPluginSettings
|
||||
public class JsonRPCPluginSettings : ISavable
|
||||
{
|
||||
public required JsonRpcConfigurationModel? Configuration { get; init; }
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
// If can parse the default value to bool, use it, otherwise use false
|
||||
: value is string stringValue && bool.TryParse(stringValue, out var boolValueFromString)
|
||||
&& boolValueFromString;
|
||||
checkBox.Dispatcher.Invoke(() =>checkBox.IsChecked = isChecked);
|
||||
checkBox.Dispatcher.Invoke(() => checkBox.IsChecked = isChecked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -154,8 +154,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public Control CreateSettingPanel()
|
||||
{
|
||||
// No need to check if NeedCreateSettingPanel is true because CreateSettingPanel will only be called if it's true
|
||||
// if (!NeedCreateSettingPanel()) return null;
|
||||
if (!NeedCreateSettingPanel()) return null!;
|
||||
|
||||
// Create main grid with two columns (Column 1: Auto, Column 2: *)
|
||||
var mainPanel = new Grid { Margin = SettingPanelMargin, VerticalAlignment = VerticalAlignment.Center };
|
||||
|
|
|
|||
|
|
@ -10,20 +10,20 @@ using Microsoft.VisualStudio.Threading;
|
|||
using StreamJsonRpc;
|
||||
using IAsyncDisposable = System.IAsyncDisposable;
|
||||
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
internal abstract class JsonRPCPluginV2 : JsonRPCPluginBase, IAsyncDisposable, IAsyncReloadable, IResultUpdated
|
||||
{
|
||||
public const string JsonRpc = "JsonRPC";
|
||||
|
||||
private static readonly string ClassName = nameof(JsonRPCPluginV2);
|
||||
|
||||
protected abstract IDuplexPipe ClientPipe { get; set; }
|
||||
|
||||
protected StreamReader ErrorStream { get; set; }
|
||||
|
||||
private JsonRpc RPC { get; set; }
|
||||
|
||||
|
||||
protected override async Task<bool> ExecuteResultAsync(JsonRPCResult result)
|
||||
{
|
||||
var res = await RPC.InvokeAsync<JsonRPCExecuteResponse>(result.JsonRPCAction.Method,
|
||||
|
|
@ -55,7 +55,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return results;
|
||||
}
|
||||
|
||||
|
||||
public override async Task InitAsync(PluginInitContext context)
|
||||
{
|
||||
await base.InitAsync(context);
|
||||
|
|
@ -88,7 +87,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
protected abstract MessageHandlerType MessageHandler { get; }
|
||||
|
||||
|
||||
private void SetupJsonRPC()
|
||||
{
|
||||
var formatter = new SystemTextJsonFormatter { JsonSerializerOptions = RequestSerializeOption };
|
||||
|
|
@ -118,8 +116,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
await RPC.InvokeAsync("reload_data", Context);
|
||||
}
|
||||
catch (RemoteMethodNotFoundException e)
|
||||
catch (RemoteMethodNotFoundException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (ConnectionLostException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.API.LogException(ClassName, $"Failed to call reload_data for plugin {Context.CurrentPluginMetadata.Name}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -129,8 +136,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
await RPC.InvokeAsync("close");
|
||||
}
|
||||
catch (RemoteMethodNotFoundException e)
|
||||
catch (RemoteMethodNotFoundException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (ConnectionLostException)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Context.API.LogException(ClassName, $"Failed to call close for plugin {Context.CurrentPluginMetadata.Name}", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
|||
{
|
||||
public class JsonRPCPublicAPI
|
||||
{
|
||||
private IPublicAPI _api;
|
||||
private readonly IPublicAPI _api;
|
||||
|
||||
public JsonRPCPublicAPI(IPublicAPI api)
|
||||
{
|
||||
|
|
@ -104,7 +104,6 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
|||
return _api.GetAllPlugins();
|
||||
}
|
||||
|
||||
|
||||
public MatchResult FuzzySearch(string query, string stringToCompare)
|
||||
{
|
||||
return _api.FuzzySearch(query, stringToCompare);
|
||||
|
|
@ -156,6 +155,11 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
|||
_api.LogWarn(className, message, methodName);
|
||||
}
|
||||
|
||||
public void LogError(string className, string message, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
_api.LogError(className, message, methodName);
|
||||
}
|
||||
|
||||
public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null)
|
||||
{
|
||||
_api.OpenDirectory(DirectoryPath, FileNameOrFilePath);
|
||||
|
|
@ -185,5 +189,10 @@ namespace Flow.Launcher.Core.Plugin.JsonRPCV2Models
|
|||
{
|
||||
_api.StopLoadingBar();
|
||||
}
|
||||
|
||||
public void SavePluginCaches()
|
||||
{
|
||||
_api.SavePluginCaches();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,14 +3,20 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.IO;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Text.Json;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
internal abstract class PluginConfig
|
||||
{
|
||||
private static readonly string ClassName = nameof(PluginConfig);
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
/// <summary>
|
||||
/// Parse plugin metadata in the given directories
|
||||
/// </summary>
|
||||
|
|
@ -32,7 +38,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginConfig.ParsePLuginConfigs|Can't delete <{directory}>", e);
|
||||
API.LogException(ClassName, $"Can't delete <{directory}>", e);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -49,11 +55,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
duplicateList
|
||||
.ForEach(
|
||||
x => Log.Warn("PluginConfig",
|
||||
string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " +
|
||||
"not loaded due to version not the highest of the duplicates",
|
||||
x.Name, x.ID, x.Version),
|
||||
"GetUniqueLatestPluginMetadata"));
|
||||
x => API.LogWarn(ClassName,
|
||||
string.Format("Duplicate plugin name: {0}, id: {1}, version: {2} " +
|
||||
"not loaded due to version not the highest of the duplicates",
|
||||
x.Name, x.ID, x.Version),
|
||||
"GetUniqueLatestPluginMetadata"));
|
||||
|
||||
return uniqueList;
|
||||
}
|
||||
|
|
@ -101,7 +107,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
string configPath = Path.Combine(pluginDirectory, Constant.PluginMetadataFileName);
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|Didn't find config file <{configPath}>");
|
||||
API.LogError(ClassName, $"Didn't find config file <{configPath}>");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -117,19 +123,19 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginConfig.GetPluginMetadata|invalid json for config <{configPath}>", e);
|
||||
API.LogException(ClassName, $"Invalid json for config <{configPath}>", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!AllowedLanguage.IsAllowed(metadata.Language))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|Invalid language <{metadata.Language}> for config <{configPath}>");
|
||||
API.LogError(ClassName, $"Invalid language <{metadata.Language}> for config <{configPath}>");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!File.Exists(metadata.ExecuteFilePath))
|
||||
{
|
||||
Log.Error($"|PluginConfig.GetPluginMetadata|execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
|
||||
API.LogError(ClassName, $"Execute file path didn't exist <{metadata.ExecuteFilePath}> for conifg <{configPath}");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ using System.Threading.Tasks;
|
|||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.ExternalPlugins;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using IRemovable = Flow.Launcher.Core.Storage.IRemovable;
|
||||
using ISavable = Flow.Launcher.Plugin.ISavable;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
|
|
@ -22,7 +22,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// </summary>
|
||||
public static class PluginManager
|
||||
{
|
||||
private static readonly string ClassName = nameof(PluginManager);
|
||||
|
||||
private static IEnumerable<PluginPair> _contextMenuPlugins;
|
||||
private static IEnumerable<PluginPair> _homePlugins;
|
||||
|
||||
public static List<PluginPair> AllPlugins { get; private set; }
|
||||
public static readonly HashSet<PluginPair> GlobalPlugins = new();
|
||||
|
|
@ -34,7 +37,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
private static PluginsSettings Settings;
|
||||
private static List<PluginMetadata> _metadatas;
|
||||
private static List<string> _modifiedPlugins = new();
|
||||
private static readonly List<string> _modifiedPlugins = new();
|
||||
|
||||
/// <summary>
|
||||
/// Directories that will hold Flow Launcher plugin directory
|
||||
|
|
@ -58,13 +61,21 @@ namespace Flow.Launcher.Core.Plugin
|
|||
/// </summary>
|
||||
public static void Save()
|
||||
{
|
||||
foreach (var plugin in AllPlugins)
|
||||
foreach (var pluginPair in AllPlugins)
|
||||
{
|
||||
var savable = plugin.Plugin as ISavable;
|
||||
savable?.Save();
|
||||
var savable = pluginPair.Plugin as ISavable;
|
||||
try
|
||||
{
|
||||
savable?.Save();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to save plugin {pluginPair.Metadata.Name}", e);
|
||||
}
|
||||
}
|
||||
|
||||
API.SavePluginSettings();
|
||||
API.SavePluginCaches();
|
||||
}
|
||||
|
||||
public static async ValueTask DisposePluginsAsync()
|
||||
|
|
@ -77,14 +88,21 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
private static async Task DisposePluginAsync(PluginPair pluginPair)
|
||||
{
|
||||
switch (pluginPair.Plugin)
|
||||
try
|
||||
{
|
||||
case IDisposable disposable:
|
||||
disposable.Dispose();
|
||||
break;
|
||||
case IAsyncDisposable asyncDisposable:
|
||||
await asyncDisposable.DisposeAsync();
|
||||
break;
|
||||
switch (pluginPair.Plugin)
|
||||
{
|
||||
case IDisposable disposable:
|
||||
disposable.Dispose();
|
||||
break;
|
||||
case IAsyncDisposable asyncDisposable:
|
||||
await asyncDisposable.DisposeAsync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to dispose plugin {pluginPair.Metadata.Name}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,11 +187,21 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
if (AllowedLanguage.IsDotNet(metadata.Language))
|
||||
{
|
||||
if (string.IsNullOrEmpty(metadata.AssemblyName))
|
||||
{
|
||||
API.LogWarn(ClassName, $"AssemblyName is empty for plugin with metadata: {metadata.Name}");
|
||||
continue; // Skip if AssemblyName is not set, which can happen for erroneous plugins
|
||||
}
|
||||
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.AssemblyName);
|
||||
metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.AssemblyName);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (string.IsNullOrEmpty(metadata.Name))
|
||||
{
|
||||
API.LogWarn(ClassName, $"Name is empty for plugin with metadata: {metadata.Name}");
|
||||
continue; // Skip if Name is not set, which can happen for erroneous plugins
|
||||
}
|
||||
metadata.PluginSettingsDirectoryPath = Path.Combine(DataLocation.PluginSettingsDirectory, metadata.Name);
|
||||
metadata.PluginCacheDirectoryPath = Path.Combine(DataLocation.PluginCacheDirectory, metadata.Name);
|
||||
}
|
||||
|
|
@ -192,24 +220,37 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
try
|
||||
{
|
||||
var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.InitializePlugins|Init method time cost for <{pair.Metadata.Name}>",
|
||||
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Init method time cost for <{pair.Metadata.Name}>",
|
||||
() => pair.Plugin.InitAsync(new PluginInitContext(pair.Metadata, API)));
|
||||
|
||||
pair.Metadata.InitTime += milliseconds;
|
||||
Log.Info(
|
||||
$"|PluginManager.InitializePlugins|Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
|
||||
API.LogInfo(ClassName,
|
||||
$"Total init cost for <{pair.Metadata.Name}> is <{pair.Metadata.InitTime}ms>");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(nameof(PluginManager), $"Fail to Init plugin: {pair.Metadata.Name}", e);
|
||||
pair.Metadata.Disabled = true;
|
||||
failedPlugins.Enqueue(pair);
|
||||
API.LogException(ClassName, $"Fail to Init plugin: {pair.Metadata.Name}", e);
|
||||
if (pair.Metadata.Disabled && pair.Metadata.HomeDisabled)
|
||||
{
|
||||
// If this plugin is already disabled, do not show error message again
|
||||
// Or else it will be shown every time
|
||||
API.LogDebug(ClassName, $"Skipped init for <{pair.Metadata.Name}> due to error");
|
||||
}
|
||||
else
|
||||
{
|
||||
pair.Metadata.Disabled = true;
|
||||
pair.Metadata.HomeDisabled = true;
|
||||
failedPlugins.Enqueue(pair);
|
||||
API.LogDebug(ClassName, $"Disable plugin <{pair.Metadata.Name}> because init failed");
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
await Task.WhenAll(InitTasks);
|
||||
|
||||
_contextMenuPlugins = GetPluginsForInterface<IContextMenu>();
|
||||
_homePlugins = GetPluginsForInterface<IAsyncHomeQuery>();
|
||||
|
||||
foreach (var plugin in AllPlugins)
|
||||
{
|
||||
// set distinct on each plugin's action keywords helps only firing global(*) and action keywords once where a plugin
|
||||
|
|
@ -257,6 +298,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
};
|
||||
}
|
||||
|
||||
public static ICollection<PluginPair> ValidPluginsForHomeQuery()
|
||||
{
|
||||
return _homePlugins.ToList();
|
||||
}
|
||||
|
||||
public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Query query, CancellationToken token)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
|
|
@ -264,7 +310,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
try
|
||||
{
|
||||
var milliseconds = await Stopwatch.DebugAsync($"|PluginManager.QueryForPlugin|Cost for {metadata.Name}",
|
||||
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
|
||||
async () => results = await pair.Plugin.QueryAsync(query, token).ConfigureAwait(false));
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
|
|
@ -288,7 +334,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
Title = $"{metadata.Name}: Failed to respond!",
|
||||
SubTitle = "Select this result for more info",
|
||||
IcoPath = Flow.Launcher.Infrastructure.Constant.ErrorIcon,
|
||||
IcoPath = Constant.ErrorIcon,
|
||||
PluginDirectory = metadata.PluginDirectory,
|
||||
ActionKeywordAssigned = query.ActionKeyword,
|
||||
PluginID = metadata.ID,
|
||||
|
|
@ -301,6 +347,36 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return results;
|
||||
}
|
||||
|
||||
public static async Task<List<Result>> QueryHomeForPluginAsync(PluginPair pair, Query query, CancellationToken token)
|
||||
{
|
||||
var results = new List<Result>();
|
||||
var metadata = pair.Metadata;
|
||||
|
||||
try
|
||||
{
|
||||
var milliseconds = await API.StopwatchLogDebugAsync(ClassName, $"Cost for {metadata.Name}",
|
||||
async () => results = await ((IAsyncHomeQuery)pair.Plugin).HomeQueryAsync(token).ConfigureAwait(false));
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
if (results == null)
|
||||
return null;
|
||||
UpdatePluginMetadata(results, metadata, query);
|
||||
|
||||
token.ThrowIfCancellationRequested();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// null will be fine since the results will only be added into queue if the token hasn't been cancelled
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to query home for plugin: {metadata.Name}", e);
|
||||
return null;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
public static void UpdatePluginMetadata(IReadOnlyList<Result> results, PluginMetadata metadata, Query query)
|
||||
{
|
||||
foreach (var r in results)
|
||||
|
|
@ -352,8 +428,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(
|
||||
$"|PluginManager.GetContextMenusForPlugin|Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
|
||||
API.LogException(ClassName,
|
||||
$"Can't load context menus for plugin <{pluginPair.Metadata.Name}>",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
|
@ -361,12 +437,17 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return results;
|
||||
}
|
||||
|
||||
public static bool IsHomePlugin(string id)
|
||||
{
|
||||
return _homePlugins.Any(p => p.Metadata.ID == id);
|
||||
}
|
||||
|
||||
public static bool ActionKeywordRegistered(string actionKeyword)
|
||||
{
|
||||
// this method is only checking for action keywords (defined as not '*') registration
|
||||
// hence the actionKeyword != Query.GlobalPluginWildcardSign logic
|
||||
return actionKeyword != Query.GlobalPluginWildcardSign
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
return actionKeyword != Query.GlobalPluginWildcardSign
|
||||
&& NonGlobalPlugins.ContainsKey(actionKeyword);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -545,7 +626,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.InstallPlugin|Failed to delete temp folder {tempFolderPluginPath}", e);
|
||||
API.LogException(ClassName, $"Failed to delete temp folder {tempFolderPluginPath}", e);
|
||||
}
|
||||
|
||||
if (checkModified)
|
||||
|
|
@ -575,11 +656,11 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
if (removePluginSettings)
|
||||
{
|
||||
// For dotnet plugins, we need to remove their PluginJsonStorage instance
|
||||
if (AllowedLanguage.IsDotNet(plugin.Language))
|
||||
// For dotnet plugins, we need to remove their PluginJsonStorage and PluginBinaryStorage instances
|
||||
if (AllowedLanguage.IsDotNet(plugin.Language) && API is IRemovable removable)
|
||||
{
|
||||
var method = API.GetType().GetMethod("RemovePluginSettings");
|
||||
method?.Invoke(API, new object[] { plugin.AssemblyName });
|
||||
removable.RemovePluginSettings(plugin.AssemblyName);
|
||||
removable.RemovePluginCaches(plugin.PluginCacheDirectoryPath);
|
||||
}
|
||||
|
||||
try
|
||||
|
|
@ -590,7 +671,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin settings folder for {plugin.Name}", e);
|
||||
API.LogException(ClassName, $"Failed to delete plugin settings folder for {plugin.Name}", e);
|
||||
API.ShowMsg(API.GetTranslation("failedToRemovePluginSettingsTitle"),
|
||||
string.Format(API.GetTranslation("failedToRemovePluginSettingsMessage"), plugin.Name));
|
||||
}
|
||||
|
|
@ -606,7 +687,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginManager.UninstallPlugin|Failed to delete plugin cache folder for {plugin.Name}", e);
|
||||
API.LogException(ClassName, $"Failed to delete plugin cache folder for {plugin.Name}", e);
|
||||
API.ShowMsg(API.GetTranslation("failedToRemovePluginCacheTitle"),
|
||||
string.Format(API.GetTranslation("failedToRemovePluginCacheMessage"), plugin.Name));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,12 +11,17 @@ using Flow.Launcher.Infrastructure.Logger;
|
|||
#pragma warning restore IDE0005
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
public static class PluginsLoader
|
||||
{
|
||||
private static readonly string ClassName = nameof(PluginsLoader);
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
|
||||
{
|
||||
var dotnetPlugins = DotNetPlugins(metadatas);
|
||||
|
|
@ -59,8 +64,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
foreach (var metadata in metadatas)
|
||||
{
|
||||
var milliseconds = Stopwatch.Debug(
|
||||
$"|PluginsLoader.DotNetPlugins|Constructor init cost for {metadata.Name}", () =>
|
||||
var milliseconds = API.StopwatchLogDebug(ClassName, $"Constructor init cost for {metadata.Name}", () =>
|
||||
{
|
||||
Assembly assembly = null;
|
||||
IAsyncPlugin plugin = null;
|
||||
|
|
@ -85,19 +89,19 @@ namespace Flow.Launcher.Core.Plugin
|
|||
#else
|
||||
catch (Exception e) when (assembly == null)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
Log.Exception(ClassName, $"Couldn't load assembly for the plugin: {metadata.Name}", e);
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
|
||||
Log.Exception(ClassName, $"Can't find the required IPlugin interface for the plugin: <{metadata.Name}>", e);
|
||||
}
|
||||
catch (ReflectionTypeLoadException e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
|
||||
Log.Exception(ClassName, $"The GetTypes method was unable to load assembly types for the plugin: <{metadata.Name}>", e);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|PluginsLoader.DotNetPlugins|The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
|
||||
Log.Exception(ClassName, $"The following plugin has errored and can not be loaded: <{metadata.Name}>", e);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,19 @@
|
|||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipelines;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Meziantou.Framework.Win32;
|
||||
using Microsoft.VisualBasic.ApplicationServices;
|
||||
using Nerdbank.Streams;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
internal abstract class ProcessStreamPluginV2 : JsonRPCPluginV2
|
||||
{
|
||||
private static JobObject _jobObject = new JobObject();
|
||||
private static readonly JobObject _jobObject = new();
|
||||
|
||||
static ProcessStreamPluginV2()
|
||||
{
|
||||
|
|
@ -66,11 +64,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
ClientPipe = new DuplexPipe(reader, writer);
|
||||
}
|
||||
|
||||
|
||||
public override async Task ReloadDataAsync()
|
||||
{
|
||||
var oldProcess = ClientProcess;
|
||||
ClientProcess = Process.Start(StartInfo);
|
||||
ClientProcess = Process.Start(StartInfo)!;
|
||||
ArgumentNullException.ThrowIfNull(ClientProcess);
|
||||
SetupPipe(ClientProcess);
|
||||
await base.ReloadDataAsync();
|
||||
|
|
@ -79,7 +76,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
oldProcess.Dispose();
|
||||
}
|
||||
|
||||
|
||||
public override async ValueTask DisposeAsync()
|
||||
{
|
||||
await base.DisposeAsync();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
|
|
@ -8,10 +8,24 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
public static Query Build(string text, Dictionary<string, PluginPair> nonGlobalPlugins)
|
||||
{
|
||||
// home query
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
return new Query()
|
||||
{
|
||||
Search = string.Empty,
|
||||
RawQuery = string.Empty,
|
||||
SearchTerms = Array.Empty<string>(),
|
||||
ActionKeyword = string.Empty,
|
||||
IsHomeQuery = true
|
||||
};
|
||||
}
|
||||
|
||||
// replace multiple white spaces with one white space
|
||||
var terms = text.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries);
|
||||
if (terms.Length == 0)
|
||||
{ // nothing was typed
|
||||
{
|
||||
// nothing was typed
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -21,25 +35,28 @@ namespace Flow.Launcher.Core.Plugin
|
|||
string[] searchTerms;
|
||||
|
||||
if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled)
|
||||
{ // use non global plugin for query
|
||||
{
|
||||
// use non global plugin for query
|
||||
actionKeyword = possibleActionKeyword;
|
||||
search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..].TrimStart() : string.Empty;
|
||||
searchTerms = terms[1..];
|
||||
}
|
||||
else
|
||||
{ // non action keyword
|
||||
{
|
||||
// non action keyword
|
||||
actionKeyword = string.Empty;
|
||||
search = rawQuery.TrimStart();
|
||||
searchTerms = terms;
|
||||
}
|
||||
|
||||
return new Query ()
|
||||
return new Query()
|
||||
{
|
||||
Search = search,
|
||||
RawQuery = rawQuery,
|
||||
SearchTerms = searchTerms,
|
||||
ActionKeyword = actionKeyword
|
||||
ActionKeyword = actionKeyword,
|
||||
IsHomeQuery = false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ using System.Reflection;
|
|||
using System.Windows;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.Globalization;
|
||||
|
|
@ -17,13 +16,19 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
public class Internationalization
|
||||
{
|
||||
private static readonly string ClassName = nameof(Internationalization);
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
private const string Folder = "Languages";
|
||||
private const string DefaultLanguageCode = "en";
|
||||
private const string DefaultFile = "en.xaml";
|
||||
private const string Extension = ".xaml";
|
||||
private readonly Settings _settings;
|
||||
private readonly List<string> _languageDirectories = new List<string>();
|
||||
private readonly List<ResourceDictionary> _oldResources = new List<ResourceDictionary>();
|
||||
private readonly List<string> _languageDirectories = new();
|
||||
private readonly List<ResourceDictionary> _oldResources = new();
|
||||
private readonly string SystemLanguageCode;
|
||||
|
||||
public Internationalization(Settings settings)
|
||||
|
|
@ -80,7 +85,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|Internationalization.AddPluginLanguageDirectories|Can't find plugin path <{location}> for <{plugin.Metadata.Name}>");
|
||||
API.LogError(ClassName, $"Can't find plugin path <{location}> for <{plugin.Metadata.Name}>");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,13 +149,13 @@ namespace Flow.Launcher.Core.Resource
|
|||
_settings.Language = isSystem ? Constant.SystemLanguageCode : language.LanguageCode;
|
||||
}
|
||||
|
||||
private Language GetLanguageByLanguageCode(string languageCode)
|
||||
private static Language GetLanguageByLanguageCode(string languageCode)
|
||||
{
|
||||
var lowercase = languageCode.ToLower();
|
||||
var language = AvailableLanguages.GetAvailableLanguages().FirstOrDefault(o => o.LanguageCode.ToLower() == lowercase);
|
||||
if (language == null)
|
||||
{
|
||||
Log.Error($"|Internationalization.GetLanguageByLanguageCode|Language code can't be found <{languageCode}>");
|
||||
API.LogError(ClassName, $"Language code can't be found <{languageCode}>");
|
||||
return AvailableLanguages.English;
|
||||
}
|
||||
else
|
||||
|
|
@ -239,7 +244,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
return list;
|
||||
}
|
||||
|
||||
public string GetTranslation(string key)
|
||||
public static string GetTranslation(string key)
|
||||
{
|
||||
var translation = Application.Current.TryFindResource(key);
|
||||
if (translation is string)
|
||||
|
|
@ -248,7 +253,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|Internationalization.GetTranslation|No Translation for key {key}");
|
||||
API.LogError(ClassName, $"No Translation for key {key}");
|
||||
return $"No Translation for key {key}";
|
||||
}
|
||||
}
|
||||
|
|
@ -257,8 +262,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
foreach (var p in PluginManager.GetPluginsForInterface<IPluginI18n>())
|
||||
{
|
||||
var pluginI18N = p.Plugin as IPluginI18n;
|
||||
if (pluginI18N == null) return;
|
||||
if (p.Plugin is not IPluginI18n pluginI18N) return;
|
||||
try
|
||||
{
|
||||
p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
|
||||
|
|
@ -267,31 +271,31 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|Internationalization.UpdatePluginMetadataTranslations|Failed for <{p.Metadata.Name}>", e);
|
||||
API.LogException(ClassName, $"Failed for <{p.Metadata.Name}>", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string LanguageFile(string folder, string language)
|
||||
private static string LanguageFile(string folder, string language)
|
||||
{
|
||||
if (Directory.Exists(folder))
|
||||
{
|
||||
string path = Path.Combine(folder, language);
|
||||
var path = Path.Combine(folder, language);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|Internationalization.LanguageFile|Language path can't be found <{path}>");
|
||||
string english = Path.Combine(folder, DefaultFile);
|
||||
API.LogError(ClassName, $"Language path can't be found <{path}>");
|
||||
var english = Path.Combine(folder, DefaultFile);
|
||||
if (File.Exists(english))
|
||||
{
|
||||
return english;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|Internationalization.LanguageFile|Default English Language path can't be found <{path}>");
|
||||
API.LogError(ClassName, $"Default English Language path can't be found <{path}>");
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
using System;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
[Obsolete("InternationalizationManager.Instance is obsolete. Use Ioc.Default.GetRequiredService<Internationalization>() instead.")]
|
||||
public static class InternationalizationManager
|
||||
{
|
||||
public static Internationalization Instance
|
||||
=> Ioc.Default.GetRequiredService<Internationalization>();
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ using System.Windows.Data;
|
|||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
[Obsolete("LocalizationConverter is obsolete. Use with Flow.Launcher.Localization NuGet package instead.")]
|
||||
public class LocalizationConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
using System.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
public class LocalizedDescriptionAttribute : DescriptionAttribute
|
||||
{
|
||||
private readonly Internationalization _translator;
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
private readonly string _resourceKey;
|
||||
|
||||
public LocalizedDescriptionAttribute(string resourceKey)
|
||||
{
|
||||
_translator = InternationalizationManager.Instance;
|
||||
_resourceKey = resourceKey;
|
||||
}
|
||||
|
||||
|
|
@ -17,7 +21,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
get
|
||||
{
|
||||
string description = _translator.GetTranslation(_resourceKey);
|
||||
string description = API.GetTranslation(_resourceKey);
|
||||
return string.IsNullOrWhiteSpace(description) ?
|
||||
string.Format("[[{0}]]", _resourceKey) : description;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ using System.Windows.Media.Effects;
|
|||
using System.Windows.Shell;
|
||||
using System.Windows.Threading;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
|
|
@ -24,6 +24,8 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
#region Properties & Fields
|
||||
|
||||
private readonly string ClassName = nameof(Theme);
|
||||
|
||||
public bool BlurEnabled { get; private set; }
|
||||
|
||||
private const string ThemeMetadataNamePrefix = "Name:";
|
||||
|
|
@ -72,20 +74,15 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
else
|
||||
{
|
||||
Log.Error("Current theme resource not found. Initializing with default theme.");
|
||||
_api.LogError(ClassName, "Current theme resource not found. Initializing with default theme.");
|
||||
_oldTheme = Constant.DefaultTheme;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Theme Resources
|
||||
|
||||
public string GetCurrentTheme()
|
||||
{
|
||||
return _settings.Theme;
|
||||
}
|
||||
|
||||
private void MakeSureThemeDirectoriesExist()
|
||||
{
|
||||
foreach (var dir in _themeDirectories.Where(dir => !Directory.Exists(dir)))
|
||||
|
|
@ -96,7 +93,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|Theme.MakesureThemeDirectoriesExist|Exception when create directory <{dir}>", e);
|
||||
_api.LogException(ClassName, $"Exception when create directory <{dir}>", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -127,9 +124,9 @@ namespace Flow.Launcher.Core.Resource
|
|||
try
|
||||
{
|
||||
// Load a ResourceDictionary for the specified theme.
|
||||
var themeName = GetCurrentTheme();
|
||||
var themeName = _settings.Theme;
|
||||
var dict = GetThemeResourceDictionary(themeName);
|
||||
|
||||
|
||||
// Apply font settings to the theme resource.
|
||||
ApplyFontSettings(dict);
|
||||
UpdateResourceDictionary(dict);
|
||||
|
|
@ -139,7 +136,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("Error occurred while updating theme fonts", e);
|
||||
_api.LogException(ClassName, "Error occurred while updating theme fonts", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -155,11 +152,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
var fontStyle = FontHelper.GetFontStyleFromInvariantStringOrNormal(_settings.QueryBoxFontStyle);
|
||||
var fontWeight = FontHelper.GetFontWeightFromInvariantStringOrNormal(_settings.QueryBoxFontWeight);
|
||||
var fontStretch = FontHelper.GetFontStretchFromInvariantStringOrNormal(_settings.QueryBoxFontStretch);
|
||||
|
||||
|
||||
SetFontProperties(queryBoxStyle, fontFamily, fontStyle, fontWeight, fontStretch, true);
|
||||
SetFontProperties(querySuggestionBoxStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||
}
|
||||
|
||||
|
||||
if (dict["ItemTitleStyle"] is Style resultItemStyle &&
|
||||
dict["ItemTitleSelectedStyle"] is Style resultItemSelectedStyle &&
|
||||
dict["ItemHotkeyStyle"] is Style resultHotkeyItemStyle &&
|
||||
|
|
@ -175,7 +172,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
SetFontProperties(resultHotkeyItemStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||
SetFontProperties(resultHotkeyItemSelectedStyle, fontFamily, fontStyle, fontWeight, fontStretch, false);
|
||||
}
|
||||
|
||||
|
||||
if (dict["ItemSubTitleStyle"] is Style resultSubItemStyle &&
|
||||
dict["ItemSubTitleSelectedStyle"] is Style resultSubItemSelectedStyle)
|
||||
{
|
||||
|
|
@ -200,7 +197,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
// First, find the setters to remove and store them in a list
|
||||
var settersToRemove = style.Setters
|
||||
.OfType<Setter>()
|
||||
.Where(setter =>
|
||||
.Where(setter =>
|
||||
setter.Property == Control.FontFamilyProperty ||
|
||||
setter.Property == Control.FontStyleProperty ||
|
||||
setter.Property == Control.FontWeightProperty ||
|
||||
|
|
@ -230,18 +227,18 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
var settersToRemove = style.Setters
|
||||
.OfType<Setter>()
|
||||
.Where(setter =>
|
||||
.Where(setter =>
|
||||
setter.Property == TextBlock.FontFamilyProperty ||
|
||||
setter.Property == TextBlock.FontStyleProperty ||
|
||||
setter.Property == TextBlock.FontWeightProperty ||
|
||||
setter.Property == TextBlock.FontStretchProperty)
|
||||
.ToList();
|
||||
|
||||
|
||||
foreach (var setter in settersToRemove)
|
||||
{
|
||||
style.Setters.Remove(setter);
|
||||
}
|
||||
|
||||
|
||||
style.Setters.Add(new Setter(TextBlock.FontFamilyProperty, fontFamily));
|
||||
style.Setters.Add(new Setter(TextBlock.FontStyleProperty, fontStyle));
|
||||
style.Setters.Add(new Setter(TextBlock.FontWeightProperty, fontWeight));
|
||||
|
|
@ -328,9 +325,9 @@ namespace Flow.Launcher.Core.Resource
|
|||
return dict;
|
||||
}
|
||||
|
||||
private ResourceDictionary GetCurrentResourceDictionary()
|
||||
public ResourceDictionary GetCurrentResourceDictionary()
|
||||
{
|
||||
return GetResourceDictionary(GetCurrentTheme());
|
||||
return GetResourceDictionary(_settings.Theme);
|
||||
}
|
||||
|
||||
private ThemeData GetThemeDataFromPath(string path)
|
||||
|
|
@ -383,9 +380,20 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
#endregion
|
||||
|
||||
#region Load & Change
|
||||
#region Get & Change Theme
|
||||
|
||||
public List<ThemeData> LoadAvailableThemes()
|
||||
public ThemeData GetCurrentTheme()
|
||||
{
|
||||
var themes = GetAvailableThemes();
|
||||
var matchingTheme = themes.FirstOrDefault(t => t.FileNameWithoutExtension == _settings.Theme);
|
||||
if (matchingTheme == null)
|
||||
{
|
||||
_api.LogWarn(ClassName, $"No matching theme found for '{_settings.Theme}'. Falling back to the first available theme.");
|
||||
}
|
||||
return matchingTheme ?? themes.FirstOrDefault();
|
||||
}
|
||||
|
||||
public List<ThemeData> GetAvailableThemes()
|
||||
{
|
||||
List<ThemeData> themes = new List<ThemeData>();
|
||||
foreach (var themeDirectory in _themeDirectories)
|
||||
|
|
@ -403,7 +411,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
public bool ChangeTheme(string theme = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(theme))
|
||||
theme = GetCurrentTheme();
|
||||
theme = _settings.Theme;
|
||||
|
||||
string path = GetThemePath(theme);
|
||||
try
|
||||
|
|
@ -413,7 +421,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
// Retrieve theme resource – always use the resource with font settings applied.
|
||||
var resourceDict = GetResourceDictionary(theme);
|
||||
|
||||
|
||||
UpdateResourceDictionary(resourceDict);
|
||||
|
||||
_settings.Theme = theme;
|
||||
|
|
@ -426,14 +434,14 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
BlurEnabled = IsBlurTheme();
|
||||
|
||||
// Can only apply blur but here also apply drop shadow effect to avoid possible drop shadow effect issues
|
||||
// Apply blur and drop shadow effect so that we do not need to call it again
|
||||
_ = RefreshFrameAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> path can't be found");
|
||||
_api.LogError(ClassName, $"Theme <{theme}> path can't be found");
|
||||
if (theme != Constant.DefaultTheme)
|
||||
{
|
||||
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_path_not_exists"), theme));
|
||||
|
|
@ -443,7 +451,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
catch (XamlParseException)
|
||||
{
|
||||
Log.Error($"|Theme.ChangeTheme|Theme <{theme}> fail to parse");
|
||||
_api.LogError(ClassName, $"Theme <{theme}> fail to parse");
|
||||
if (theme != Constant.DefaultTheme)
|
||||
{
|
||||
_api.ShowMsgBox(string.Format(_api.GetTranslation("theme_load_failure_parse_error"), theme));
|
||||
|
|
@ -591,7 +599,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
AutoDropShadow(useDropShadowEffect);
|
||||
}
|
||||
SetBlurForWindow(GetCurrentTheme(), backdropType);
|
||||
SetBlurForWindow(_settings.Theme, backdropType);
|
||||
|
||||
if (!BlurEnabled)
|
||||
{
|
||||
|
|
@ -610,7 +618,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
// Get the actual backdrop type and drop shadow effect settings
|
||||
var (backdropType, _) = GetActualValue();
|
||||
|
||||
SetBlurForWindow(GetCurrentTheme(), backdropType);
|
||||
SetBlurForWindow(_settings.Theme, backdropType);
|
||||
}, DispatcherPriority.Render);
|
||||
}
|
||||
|
||||
|
|
@ -663,7 +671,15 @@ namespace Flow.Launcher.Core.Resource
|
|||
windowBorderStyle.Setters.Remove(windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property.Name == "Background"));
|
||||
windowBorderStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(Colors.Transparent)));
|
||||
}
|
||||
|
||||
|
||||
// For themes with blur enabled, the window border is rendered by the system, so it's treated as a simple rectangle regardless of thickness.
|
||||
//(This is to avoid issues when the window is forcibly changed to a rectangular shape during snap scenarios.)
|
||||
var cornerRadiusSetter = windowBorderStyle.Setters.OfType<Setter>().FirstOrDefault(x => x.Property == Border.CornerRadiusProperty);
|
||||
if (cornerRadiusSetter != null)
|
||||
cornerRadiusSetter.Value = new CornerRadius(0);
|
||||
else
|
||||
windowBorderStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(0)));
|
||||
|
||||
// Apply the blur effect
|
||||
Win32Helper.DWMSetBackdropForWindow(mainWindow, backdropType);
|
||||
ColorizeWindow(theme, backdropType);
|
||||
|
|
@ -764,22 +780,18 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
if (bgColor == null) return;
|
||||
|
||||
// Copy the existing WindowBorderStyle
|
||||
// Create a new Style for the preview
|
||||
var previewStyle = new Style(typeof(Border));
|
||||
if (Application.Current.Resources.Contains("WindowBorderStyle"))
|
||||
|
||||
// Get the original WindowBorderStyle
|
||||
if (Application.Current.Resources.Contains("WindowBorderStyle") &&
|
||||
Application.Current.Resources["WindowBorderStyle"] is Style originalStyle)
|
||||
{
|
||||
if (Application.Current.Resources["WindowBorderStyle"] is Style originalStyle)
|
||||
{
|
||||
foreach (var setter in originalStyle.Setters.OfType<Setter>())
|
||||
{
|
||||
previewStyle.Setters.Add(new Setter(setter.Property, setter.Value));
|
||||
}
|
||||
}
|
||||
// Copy the original style, including the base style if it exists
|
||||
CopyStyle(originalStyle, previewStyle);
|
||||
}
|
||||
|
||||
// Apply background color (remove transparency in color)
|
||||
// WPF does not allow the use of an acrylic brush within the window's internal area,
|
||||
// so transparency effects are not applied to the preview.
|
||||
Color backgroundColor = Color.FromRgb(bgColor.Value.R, bgColor.Value.G, bgColor.Value.B);
|
||||
previewStyle.Setters.Add(new Setter(Border.BackgroundProperty, new SolidColorBrush(backgroundColor)));
|
||||
|
||||
|
|
@ -790,9 +802,26 @@ namespace Flow.Launcher.Core.Resource
|
|||
previewStyle.Setters.Add(new Setter(Border.CornerRadiusProperty, new CornerRadius(5)));
|
||||
previewStyle.Setters.Add(new Setter(Border.BorderThicknessProperty, new Thickness(1)));
|
||||
}
|
||||
|
||||
// Set the new style to the resource
|
||||
Application.Current.Resources["PreviewWindowBorderStyle"] = previewStyle;
|
||||
}
|
||||
|
||||
private void CopyStyle(Style originalStyle, Style targetStyle)
|
||||
{
|
||||
// If the style is based on another style, copy the base style first
|
||||
if (originalStyle.BasedOn != null)
|
||||
{
|
||||
CopyStyle(originalStyle.BasedOn, targetStyle);
|
||||
}
|
||||
|
||||
// Copy the setters from the original style
|
||||
foreach (var setter in originalStyle.Setters.OfType<Setter>())
|
||||
{
|
||||
targetStyle.Setters.Add(new Setter(setter.Property, setter.Value));
|
||||
}
|
||||
}
|
||||
|
||||
private void ColorizeWindow(string theme, BackdropTypes backdropType)
|
||||
{
|
||||
var dict = GetThemeResourceDictionary(theme);
|
||||
|
|
@ -898,11 +927,5 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
public record ThemeData(string FileNameWithoutExtension, string Name, bool? IsDark = null, bool? HasBlur = null);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
public class TranslationConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var key = value.ToString();
|
||||
if (String.IsNullOrEmpty(key))
|
||||
return key;
|
||||
return InternationalizationManager.Instance.GetTranslation(key);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
|
||||
}
|
||||
}
|
||||
19
Flow.Launcher.Core/Storage/IRemovable.cs
Normal file
19
Flow.Launcher.Core/Storage/IRemovable.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
namespace Flow.Launcher.Core.Storage;
|
||||
|
||||
/// <summary>
|
||||
/// Remove storage instances from <see cref="Launcher.Plugin.IPublicAPI"/> instance
|
||||
/// </summary>
|
||||
public interface IRemovable
|
||||
{
|
||||
/// <summary>
|
||||
/// Remove all <see cref="Infrastructure.Storage.PluginJsonStorage{T}"/> instances of one plugin
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"></param>
|
||||
public void RemovePluginSettings(string assemblyName);
|
||||
|
||||
/// <summary>
|
||||
/// Remove all <see cref="Infrastructure.Storage.PluginBinaryStorage{T}"/> instances of one plugin
|
||||
/// </summary>
|
||||
/// <param name="cacheDirectory"></param>
|
||||
public void RemovePluginCaches(string cacheDirectory);
|
||||
}
|
||||
|
|
@ -9,12 +9,9 @@ using System.Text.Json.Serialization;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Http;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using JetBrains.Annotations;
|
||||
|
|
@ -27,6 +24,8 @@ namespace Flow.Launcher.Core
|
|||
public string GitHubReleaseRepository { get; }
|
||||
public string GitHubPrereleaseRepository { get; }
|
||||
|
||||
private static readonly string ClassName = nameof(Updater);
|
||||
|
||||
public bool UpdateToPrerelease => _settings.PrereleaseUpdateSource;
|
||||
|
||||
public string GitHubRepository => UpdateToPrerelease ? GitHubPrereleaseRepository : GitHubReleaseRepository;
|
||||
|
|
@ -61,7 +60,7 @@ namespace Flow.Launcher.Core
|
|||
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
|
||||
var currentVersion = Version.Parse(Constant.Version);
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>");
|
||||
_api.LogInfo(ClassName, $"Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>");
|
||||
|
||||
if (newReleaseVersion <= currentVersion)
|
||||
{
|
||||
|
|
@ -94,7 +93,7 @@ namespace Flow.Launcher.Core
|
|||
|
||||
var newVersionTips = NewVersionTips(newReleaseVersion.ToString());
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
|
||||
_api.LogInfo(ClassName, $"Update success:{newVersionTips}");
|
||||
|
||||
if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
|
||||
{
|
||||
|
|
@ -103,10 +102,14 @@ namespace Flow.Launcher.Core
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if ((e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException))
|
||||
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
|
||||
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
|
||||
{
|
||||
_api.LogException(ClassName, $"Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
|
||||
}
|
||||
else
|
||||
Log.Exception($"|Updater.UpdateApp|Error Occurred", e);
|
||||
{
|
||||
_api.LogException(ClassName, $"Error Occurred", e);
|
||||
}
|
||||
|
||||
if (!silentUpdate)
|
||||
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"),
|
||||
|
|
@ -139,7 +142,7 @@ namespace Flow.Launcher.Core
|
|||
|
||||
await using var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
|
||||
|
||||
var releases = await System.Text.Json.JsonSerializer.DeserializeAsync<List<GithubRelease>>(jsonStream).ConfigureAwait(false);
|
||||
var releases = await JsonSerializer.DeserializeAsync<List<GithubRelease>>(jsonStream).ConfigureAwait(false);
|
||||
var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
|
||||
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/");
|
||||
|
||||
|
|
@ -154,10 +157,9 @@ namespace Flow.Launcher.Core
|
|||
return manager;
|
||||
}
|
||||
|
||||
private static string NewVersionTips(string version)
|
||||
private string NewVersionTips(string version)
|
||||
{
|
||||
var translator = Ioc.Default.GetRequiredService<Internationalization>();
|
||||
var tips = string.Format(translator.GetTranslation("newVersionTips"), version);
|
||||
var tips = string.Format(_api.GetTranslation("newVersionTips"), version);
|
||||
|
||||
return tips;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,10 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NLog" Version="4.7.10" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SharpVectors.Wpf" Version="1.8.4.2" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
|
||||
<!--ToolGood.Words.Pinyin v3.0.2.6 results in high memory usage when search with pinyin is enabled-->
|
||||
<!--Bumping to it or higher needs to test and ensure this is no longer a problem-->
|
||||
|
|
|
|||
|
|
@ -1,30 +1,31 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Flow.Launcher.Plugin;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Http
|
||||
{
|
||||
public static class Http
|
||||
{
|
||||
private static readonly string ClassName = nameof(Http);
|
||||
|
||||
private const string UserAgent = @"Mozilla/5.0 (Trident/7.0; rv:11.0) like Gecko";
|
||||
|
||||
private static HttpClient client = new HttpClient();
|
||||
private static readonly HttpClient client = new();
|
||||
|
||||
static Http()
|
||||
{
|
||||
// need to be added so it would work on a win10 machine
|
||||
ServicePointManager.Expect100Continue = true;
|
||||
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls
|
||||
| SecurityProtocolType.Tls11
|
||||
| SecurityProtocolType.Tls12;
|
||||
| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
|
||||
|
||||
client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
|
||||
HttpClient.DefaultProxy = WebProxy;
|
||||
|
|
@ -34,7 +35,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
|
||||
public static HttpProxy Proxy
|
||||
{
|
||||
private get { return proxy; }
|
||||
private get => proxy;
|
||||
set
|
||||
{
|
||||
proxy = value;
|
||||
|
|
@ -72,13 +73,13 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
ProxyProperty.Port => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"), WebProxy.Credentials),
|
||||
ProxyProperty.UserName => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
ProxyProperty.Password => (WebProxy.Address, new NetworkCredential(Proxy.UserName, Proxy.Password)),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
_ => throw new ArgumentOutOfRangeException(null)
|
||||
};
|
||||
}
|
||||
catch (UriFormatException e)
|
||||
{
|
||||
Ioc.Default.GetRequiredService<IPublicAPI>().ShowMsg("Please try again", "Unable to parse Http Proxy");
|
||||
Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e);
|
||||
Log.Exception(ClassName, "Unable to parse Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,7 +135,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
}
|
||||
catch (HttpRequestException e)
|
||||
{
|
||||
Log.Exception("Infrastructure.Http", "Http Request Error", e, "DownloadAsync");
|
||||
Log.Exception(ClassName, "Http Request Error", e, "DownloadAsync");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -147,7 +148,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
/// <returns>The Http result as string. Null if cancellation requested</returns>
|
||||
public static Task<string> GetAsync([NotNull] string url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
Log.Debug(ClassName, $"Url <{url}>");
|
||||
return GetAsync(new Uri(url), token);
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +160,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
/// <returns>The Http result as string. Null if cancellation requested</returns>
|
||||
public static async Task<string> GetAsync([NotNull] Uri url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
Log.Debug(ClassName, $"Url <{url}>");
|
||||
using var response = await client.GetAsync(url, token);
|
||||
var content = await response.Content.ReadAsStringAsync(token);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
|
|
@ -181,7 +182,6 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
public static Task<Stream> GetStreamAsync([NotNull] string url,
|
||||
CancellationToken token = default) => GetStreamAsync(new Uri(url), token);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation.
|
||||
/// </summary>
|
||||
|
|
@ -191,7 +191,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
public static async Task<Stream> GetStreamAsync([NotNull] Uri url,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
Log.Debug(ClassName, $"Url <{url}>");
|
||||
return await client.GetStreamAsync(url, token);
|
||||
}
|
||||
|
||||
|
|
@ -202,7 +202,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
public static async Task<HttpResponseMessage> GetResponseAsync([NotNull] Uri url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
Log.Debug(ClassName, $"Url <{url}>");
|
||||
return await client.GetAsync(url, completionOption, token);
|
||||
}
|
||||
|
||||
|
|
@ -211,7 +211,27 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
/// </summary>
|
||||
public static async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken token = default)
|
||||
{
|
||||
return await client.SendAsync(request, completionOption, token);
|
||||
try
|
||||
{
|
||||
return await client.SendAsync(request, completionOption, token);
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
|
||||
}
|
||||
}
|
||||
|
||||
public static async Task<string> GetStringAsync(string url, CancellationToken token = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Debug(ClassName, $"Url <{url}>");
|
||||
return await client.GetStringAsync(url, token);
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using BitFaster.Caching.Lfu;
|
||||
|
|
@ -55,7 +53,6 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return image != null;
|
||||
}
|
||||
|
||||
|
||||
image = null;
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,11 +9,15 @@ using System.Windows.Media;
|
|||
using System.Windows.Media.Imaging;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using SharpVectors.Converters;
|
||||
using SharpVectors.Renderers.Wpf;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Image
|
||||
{
|
||||
public static class ImageLoader
|
||||
{
|
||||
private static readonly string ClassName = nameof(ImageLoader);
|
||||
|
||||
private static readonly ImageCache ImageCache = new();
|
||||
private static SemaphoreSlim storageLock { get; } = new SemaphoreSlim(1, 1);
|
||||
private static BinaryStorage<List<(string, bool)>> _storage;
|
||||
|
|
@ -25,8 +29,10 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
public static ImageSource LoadingImage { get; } = new BitmapImage(new Uri(Constant.LoadingImgIcon));
|
||||
public const int SmallIconSize = 64;
|
||||
public const int FullIconSize = 256;
|
||||
public const int FullImageSize = 320;
|
||||
|
||||
private static readonly string[] ImageExtensions = { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico" };
|
||||
private static readonly string SvgExtension = ".svg";
|
||||
|
||||
public static async Task InitializeAsync()
|
||||
{
|
||||
|
|
@ -34,6 +40,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
_hashGenerator = new ImageHashGenerator();
|
||||
|
||||
var usage = await LoadStorageToConcurrentDictionaryAsync();
|
||||
_storage.ClearData();
|
||||
|
||||
ImageCache.Initialize(usage);
|
||||
|
||||
|
|
@ -46,15 +53,14 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () =>
|
||||
await Stopwatch.InfoAsync(ClassName, "Preload images cost", async () =>
|
||||
{
|
||||
foreach (var (path, isFullImage) in usage)
|
||||
{
|
||||
await LoadAsync(path, isFullImage);
|
||||
}
|
||||
});
|
||||
Log.Info(
|
||||
$"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
|
||||
Log.Info(ClassName, $"Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +76,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Log.Exception($"|ImageLoader.SaveAsync|Failed to save image cache to file", e);
|
||||
Log.Exception(ClassName, "Failed to save image cache to file", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -165,8 +171,8 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
}
|
||||
catch (System.Exception e2)
|
||||
{
|
||||
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on first try", e);
|
||||
Log.Exception($"|ImageLoader.Load|Failed to get thumbnail for {path} on second try", e2);
|
||||
Log.Exception(ClassName, $"Failed to get thumbnail for {path} on first try", e);
|
||||
Log.Exception(ClassName, $"Failed to get thumbnail for {path} on second try", e2);
|
||||
|
||||
ImageSource image = ImageCache[Constant.MissingImgIcon, false];
|
||||
ImageCache[path, false] = image;
|
||||
|
|
@ -228,10 +234,11 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
image = LoadFullImage(path);
|
||||
type = ImageType.FullImageFile;
|
||||
}
|
||||
catch (NotSupportedException)
|
||||
catch (NotSupportedException ex)
|
||||
{
|
||||
image = Image;
|
||||
type = ImageType.Error;
|
||||
Log.Exception(ClassName, $"Failed to load image file from path {path}: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -244,6 +251,20 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
image = GetThumbnail(path, ThumbnailOptions.ThumbnailOnly);
|
||||
}
|
||||
}
|
||||
else if (extension == SvgExtension)
|
||||
{
|
||||
try
|
||||
{
|
||||
image = LoadSvgImage(path, loadFullImage);
|
||||
type = ImageType.FullImageFile;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
image = Image;
|
||||
type = ImageType.Error;
|
||||
Log.Exception(ClassName, $"Failed to load SVG image from path {path}: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
type = ImageType.File;
|
||||
|
|
@ -284,7 +305,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return ImageCache.TryGetValue(path, loadFullImage, out image);
|
||||
}
|
||||
|
||||
public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullImage = false)
|
||||
public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullImage = false, bool cacheImage = true)
|
||||
{
|
||||
var imageResult = await LoadInternalAsync(path, loadFullImage);
|
||||
|
||||
|
|
@ -300,22 +321,24 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
// image already exists
|
||||
img = ImageCache[key, loadFullImage] ?? img;
|
||||
}
|
||||
else
|
||||
else if (cacheImage)
|
||||
{
|
||||
// new guid
|
||||
|
||||
// save guid key
|
||||
GuidToKey[hash] = path;
|
||||
}
|
||||
}
|
||||
|
||||
// update cache
|
||||
ImageCache[path, loadFullImage] = img;
|
||||
if (cacheImage)
|
||||
{
|
||||
// update cache
|
||||
ImageCache[path, loadFullImage] = img;
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
private static BitmapImage LoadFullImage(string path)
|
||||
private static ImageSource LoadFullImage(string path)
|
||||
{
|
||||
BitmapImage image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
|
|
@ -324,24 +347,24 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
|
||||
image.EndInit();
|
||||
|
||||
if (image.PixelWidth > 320)
|
||||
if (image.PixelWidth > FullImageSize)
|
||||
{
|
||||
BitmapImage resizedWidth = new BitmapImage();
|
||||
resizedWidth.BeginInit();
|
||||
resizedWidth.CacheOption = BitmapCacheOption.OnLoad;
|
||||
resizedWidth.UriSource = new Uri(path);
|
||||
resizedWidth.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
|
||||
resizedWidth.DecodePixelWidth = 320;
|
||||
resizedWidth.DecodePixelWidth = FullImageSize;
|
||||
resizedWidth.EndInit();
|
||||
|
||||
if (resizedWidth.PixelHeight > 320)
|
||||
if (resizedWidth.PixelHeight > FullImageSize)
|
||||
{
|
||||
BitmapImage resizedHeight = new BitmapImage();
|
||||
resizedHeight.BeginInit();
|
||||
resizedHeight.CacheOption = BitmapCacheOption.OnLoad;
|
||||
resizedHeight.UriSource = new Uri(path);
|
||||
resizedHeight.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
|
||||
resizedHeight.DecodePixelHeight = 320;
|
||||
resizedHeight.DecodePixelHeight = FullImageSize;
|
||||
resizedHeight.EndInit();
|
||||
return resizedHeight;
|
||||
}
|
||||
|
|
@ -351,5 +374,50 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
return image;
|
||||
}
|
||||
|
||||
private static ImageSource LoadSvgImage(string path, bool loadFullImage = false)
|
||||
{
|
||||
// Set up drawing settings
|
||||
var desiredHeight = loadFullImage ? FullImageSize : SmallIconSize;
|
||||
var drawingSettings = new WpfDrawingSettings
|
||||
{
|
||||
IncludeRuntime = true,
|
||||
// Set IgnoreRootViewbox to false to respect the SVG's viewBox
|
||||
IgnoreRootViewbox = false
|
||||
};
|
||||
|
||||
// Load and render the SVG
|
||||
var converter = new FileSvgReader(drawingSettings);
|
||||
var drawing = converter.Read(new Uri(path));
|
||||
|
||||
// Calculate scale to achieve desired height
|
||||
var drawingBounds = drawing.Bounds;
|
||||
if (drawingBounds.Height <= 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Invalid SVG dimensions: Height must be greater than zero in {path}");
|
||||
}
|
||||
var scale = desiredHeight / drawingBounds.Height;
|
||||
var scaledWidth = drawingBounds.Width * scale;
|
||||
var scaledHeight = drawingBounds.Height * scale;
|
||||
|
||||
// Convert the Drawing to a Bitmap
|
||||
var drawingVisual = new DrawingVisual();
|
||||
using (DrawingContext drawingContext = drawingVisual.RenderOpen())
|
||||
{
|
||||
drawingContext.PushTransform(new ScaleTransform(scale, scale));
|
||||
drawingContext.DrawDrawing(drawing);
|
||||
}
|
||||
|
||||
// Create a RenderTargetBitmap to hold the rendered image
|
||||
var bitmap = new RenderTargetBitmap(
|
||||
(int)Math.Ceiling(scaledWidth),
|
||||
(int)Math.Ceiling(scaledHeight),
|
||||
96, // DpiX
|
||||
96, // DpiY
|
||||
PixelFormats.Pbgra32);
|
||||
bitmap.Render(drawingVisual);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ using Windows.Win32.Graphics.Gdi;
|
|||
namespace Flow.Launcher.Infrastructure.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Subclass of <see cref="Windows.Win32.UI.Shell.SIIGBF"/>
|
||||
/// Subclass of <see cref="SIIGBF"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ThumbnailOptions
|
||||
|
|
@ -31,7 +31,9 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
private static readonly Guid GUID_IShellItem = typeof(IShellItem).GUID;
|
||||
|
||||
private static readonly HRESULT S_ExtractionFailed = (HRESULT)0x8004B200;
|
||||
private static readonly HRESULT S_EXTRACTIONFAILED = (HRESULT)0x8004B200;
|
||||
|
||||
private static readonly HRESULT S_PATHNOTFOUND = (HRESULT)0x8004B205;
|
||||
|
||||
public static BitmapSource GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
|
||||
{
|
||||
|
|
@ -79,9 +81,10 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
imageFactory.GetImage(size, (SIIGBF)options, &hBitmap);
|
||||
}
|
||||
catch (COMException ex) when (ex.HResult == S_ExtractionFailed && options == ThumbnailOptions.ThumbnailOnly)
|
||||
catch (COMException ex) when (options == ThumbnailOptions.ThumbnailOnly &&
|
||||
(ex.HResult == S_PATHNOTFOUND || ex.HResult == S_EXTRACTIONFAILED))
|
||||
{
|
||||
// Fallback to IconOnly if ThumbnailOnly fails
|
||||
// Fallback to IconOnly if extraction fails or files cannot be found
|
||||
imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap);
|
||||
}
|
||||
catch (FileNotFoundException) when (options == ThumbnailOptions.ThumbnailOnly)
|
||||
|
|
@ -89,6 +92,11 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
// Fallback to IconOnly if files cannot be found
|
||||
imageFactory.GetImage(size, (SIIGBF)ThumbnailOptions.IconOnly, &hBitmap);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
// Handle other exceptions
|
||||
throw new InvalidOperationException("Failed to get thumbnail", ex);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using NLog.Targets.Wrappers;
|
||||
using System.Runtime.ExceptionServices;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Logger
|
||||
{
|
||||
|
|
@ -94,13 +94,6 @@ namespace Flow.Launcher.Infrastructure.Logger
|
|||
logger.Fatal(message);
|
||||
}
|
||||
|
||||
private static bool FormatValid(string message)
|
||||
{
|
||||
var parts = message.Split('|');
|
||||
var valid = parts.Length == 3 && !string.IsNullOrWhiteSpace(parts[1]) && !string.IsNullOrWhiteSpace(parts[2]);
|
||||
return valid;
|
||||
}
|
||||
|
||||
public static void Exception(string className, string message, System.Exception exception, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
exception = exception.Demystify();
|
||||
|
|
@ -135,57 +128,14 @@ namespace Flow.Launcher.Infrastructure.Logger
|
|||
return className;
|
||||
}
|
||||
|
||||
#if !DEBUG
|
||||
private static void ExceptionInternal(string classAndMethod, string message, System.Exception e)
|
||||
{
|
||||
var logger = LogManager.GetLogger(classAndMethod);
|
||||
|
||||
logger.Error(e, message);
|
||||
}
|
||||
|
||||
private static void LogInternal(string message, LogLevel level)
|
||||
{
|
||||
if (FormatValid(message))
|
||||
{
|
||||
var parts = message.Split('|');
|
||||
var prefix = parts[1];
|
||||
var unprefixed = parts[2];
|
||||
var logger = LogManager.GetLogger(prefix);
|
||||
logger.Log(level, unprefixed);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFaultyFormat(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// Example: "|ClassName.MethodName|Message"
|
||||
/// <param name="message">Example: "|ClassName.MethodName|Message" </param>
|
||||
/// <param name="e">Exception</param>
|
||||
public static void Exception(string message, System.Exception e)
|
||||
{
|
||||
e = e.Demystify();
|
||||
#if DEBUG
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
#else
|
||||
if (FormatValid(message))
|
||||
{
|
||||
var parts = message.Split('|');
|
||||
var prefix = parts[1];
|
||||
var unprefixed = parts[2];
|
||||
ExceptionInternal(prefix, unprefixed, e);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogFaultyFormat(message);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Example: "|ClassName.MethodName|Message"
|
||||
public static void Error(string message)
|
||||
{
|
||||
LogInternal(message, LogLevel.Error);
|
||||
}
|
||||
|
||||
public static void Error(string className, string message, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
|
|
@ -206,33 +156,15 @@ namespace Flow.Launcher.Infrastructure.Logger
|
|||
LogInternal(LogLevel.Debug, className, message, methodName);
|
||||
}
|
||||
|
||||
/// Example: "|ClassName.MethodName|Message""
|
||||
public static void Debug(string message)
|
||||
{
|
||||
LogInternal(message, LogLevel.Debug);
|
||||
}
|
||||
|
||||
public static void Info(string className, string message, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
LogInternal(LogLevel.Info, className, message, methodName);
|
||||
}
|
||||
|
||||
/// Example: "|ClassName.MethodName|Message"
|
||||
public static void Info(string message)
|
||||
{
|
||||
LogInternal(message, LogLevel.Info);
|
||||
}
|
||||
|
||||
public static void Warn(string className, string message, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
LogInternal(LogLevel.Warn, className, message, methodName);
|
||||
}
|
||||
|
||||
/// Example: "|ClassName.MethodName|Message"
|
||||
public static void Warn(string message)
|
||||
{
|
||||
LogInternal(message, LogLevel.Warn);
|
||||
}
|
||||
}
|
||||
|
||||
public enum LOGLEVEL
|
||||
|
|
|
|||
|
|
@ -11,11 +11,6 @@ GetModuleHandle
|
|||
GetKeyState
|
||||
VIRTUAL_KEY
|
||||
|
||||
WM_KEYDOWN
|
||||
WM_KEYUP
|
||||
WM_SYSKEYDOWN
|
||||
WM_SYSKEYUP
|
||||
|
||||
EnumWindows
|
||||
|
||||
DwmSetWindowAttribute
|
||||
|
|
@ -27,7 +22,7 @@ SystemParametersInfo
|
|||
|
||||
SetForegroundWindow
|
||||
|
||||
GetWindowLong
|
||||
WINDOW_LONG_PTR_INDEX
|
||||
GetForegroundWindow
|
||||
GetDesktopWindow
|
||||
GetShellWindow
|
||||
|
|
@ -47,6 +42,14 @@ MONITORINFOEXW
|
|||
|
||||
WM_ENTERSIZEMOVE
|
||||
WM_EXITSIZEMOVE
|
||||
WM_NCLBUTTONDBLCLK
|
||||
WM_SYSCOMMAND
|
||||
|
||||
SC_MAXIMIZE
|
||||
SC_MINIMIZE
|
||||
|
||||
OleInitialize
|
||||
OleUninitialize
|
||||
|
||||
GetKeyboardLayout
|
||||
GetWindowThreadProcessId
|
||||
|
|
@ -58,4 +61,8 @@ INPUTLANGCHANGE_FORWARD
|
|||
LOCALE_TRANSIENT_KEYBOARD1
|
||||
LOCALE_TRANSIENT_KEYBOARD2
|
||||
LOCALE_TRANSIENT_KEYBOARD3
|
||||
LOCALE_TRANSIENT_KEYBOARD4
|
||||
LOCALE_TRANSIENT_KEYBOARD4
|
||||
|
||||
SHParseDisplayName
|
||||
SHOpenFolderAndSelectItems
|
||||
CoTaskMemFree
|
||||
|
|
|
|||
|
|
@ -4,14 +4,16 @@ using Windows.Win32.UI.WindowsAndMessaging;
|
|||
|
||||
namespace Windows.Win32;
|
||||
|
||||
// Edited from: https://github.com/files-community/Files
|
||||
internal static partial class PInvoke
|
||||
{
|
||||
// SetWindowLong
|
||||
// Edited from: https://github.com/files-community/Files
|
||||
|
||||
[DllImport("User32", EntryPoint = "SetWindowLongW", ExactSpelling = true)]
|
||||
static extern int _SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);
|
||||
private static extern int _SetWindowLong(HWND hWnd, int nIndex, int dwNewLong);
|
||||
|
||||
[DllImport("User32", EntryPoint = "SetWindowLongPtrW", ExactSpelling = true)]
|
||||
static extern nint _SetWindowLongPtr(HWND hWnd, int nIndex, nint dwNewLong);
|
||||
private static extern nint _SetWindowLongPtr(HWND hWnd, int nIndex, nint dwNewLong);
|
||||
|
||||
// NOTE:
|
||||
// CsWin32 doesn't generate SetWindowLong on other than x86 and vice versa.
|
||||
|
|
@ -22,4 +24,22 @@ internal static partial class PInvoke
|
|||
? _SetWindowLong(hWnd, (int)nIndex, (int)dwNewLong)
|
||||
: _SetWindowLongPtr(hWnd, (int)nIndex, dwNewLong);
|
||||
}
|
||||
|
||||
// GetWindowLong
|
||||
|
||||
[DllImport("User32", EntryPoint = "GetWindowLongW", ExactSpelling = true)]
|
||||
private static extern int _GetWindowLong(HWND hWnd, int nIndex);
|
||||
|
||||
[DllImport("User32", EntryPoint = "GetWindowLongPtrW", ExactSpelling = true)]
|
||||
private static extern nint _GetWindowLongPtr(HWND hWnd, int nIndex);
|
||||
|
||||
// NOTE:
|
||||
// CsWin32 doesn't generate GetWindowLong on other than x86 and vice versa.
|
||||
// For more info, visit https://github.com/microsoft/CsWin32/issues/882
|
||||
public static unsafe nint GetWindowLongPtr(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
|
||||
{
|
||||
return sizeof(nint) is 4
|
||||
? _GetWindowLong(hWnd, (int)nIndex)
|
||||
: _GetWindowLongPtr(hWnd, (int)nIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
||||
|
|
@ -7,91 +7,54 @@ namespace Flow.Launcher.Infrastructure
|
|||
{
|
||||
public static class Stopwatch
|
||||
{
|
||||
private static readonly Dictionary<string, long> Count = new Dictionary<string, long>();
|
||||
private static readonly object Locker = new object();
|
||||
/// <summary>
|
||||
/// This stopwatch will appear only in Debug mode
|
||||
/// </summary>
|
||||
public static long Debug(string message, Action action)
|
||||
public static long Debug(string className, string message, Action action, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Debug(info);
|
||||
Log.Debug(className, $"{message} <{milliseconds}ms>", methodName);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This stopwatch will appear only in Debug mode
|
||||
/// </summary>
|
||||
public static async Task<long> DebugAsync(string message, Func<Task> action)
|
||||
public static async Task<long> DebugAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
await action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Debug(info);
|
||||
Log.Debug(className, $"{message} <{milliseconds}ms>", methodName);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public static long Normal(string message, Action action)
|
||||
public static long Info(string className, string message, Action action, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Info(info);
|
||||
Log.Info(className, $"{message} <{milliseconds}ms>", methodName);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
public static async Task<long> NormalAsync(string message, Func<Task> action)
|
||||
public static async Task<long> InfoAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "")
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
await action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
string info = $"{message} <{milliseconds}ms>";
|
||||
Log.Info(info);
|
||||
Log.Info(className, $"{message} <{milliseconds}ms>", methodName);
|
||||
return milliseconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static void StartCount(string name, Action action)
|
||||
{
|
||||
var stopWatch = new System.Diagnostics.Stopwatch();
|
||||
stopWatch.Start();
|
||||
action();
|
||||
stopWatch.Stop();
|
||||
var milliseconds = stopWatch.ElapsedMilliseconds;
|
||||
lock (Locker)
|
||||
{
|
||||
if (Count.ContainsKey(name))
|
||||
{
|
||||
Count[name] += milliseconds;
|
||||
}
|
||||
else
|
||||
{
|
||||
Count[name] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void EndCount()
|
||||
{
|
||||
foreach (var key in Count.Keys)
|
||||
{
|
||||
string info = $"{key} already cost {Count[key]}ms";
|
||||
Log.Debug(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using MemoryPack;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -12,44 +16,67 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
/// Normally, it has better performance, but not readable
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// It utilize MemoryPack, which means the object must be MemoryPackSerializable <see href="https://github.com/Cysharp/MemoryPack"/>
|
||||
/// It utilizes MemoryPack, which means the object must be MemoryPackSerializable <see href="https://github.com/Cysharp/MemoryPack"/>
|
||||
/// </remarks>
|
||||
public class BinaryStorage<T>
|
||||
public class BinaryStorage<T> : ISavable
|
||||
{
|
||||
private static readonly string ClassName = "BinaryStorage";
|
||||
|
||||
protected T? Data;
|
||||
|
||||
public const string FileSuffix = ".cache";
|
||||
|
||||
// Let the derived class to set the file path
|
||||
public BinaryStorage(string filename, string directoryPath = null)
|
||||
{
|
||||
directoryPath ??= DataLocation.CacheDirectory;
|
||||
FilesFolders.ValidateDirectory(directoryPath);
|
||||
protected string FilePath { get; init; } = null!;
|
||||
|
||||
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
|
||||
protected string DirectoryPath { get; init; } = null!;
|
||||
|
||||
// Let the derived class to set the file path
|
||||
protected BinaryStorage()
|
||||
{
|
||||
}
|
||||
|
||||
public string FilePath { get; }
|
||||
public BinaryStorage(string filename)
|
||||
{
|
||||
DirectoryPath = DataLocation.CacheDirectory;
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
|
||||
}
|
||||
|
||||
// Let the old Program plugin get this constructor
|
||||
[Obsolete("This constructor is obsolete. Use BinaryStorage(string filename) instead.")]
|
||||
public BinaryStorage(string filename, string directoryPath = null!)
|
||||
{
|
||||
DirectoryPath = directoryPath ?? DataLocation.CacheDirectory;
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
|
||||
}
|
||||
|
||||
public async ValueTask<T> TryLoadAsync(T defaultData)
|
||||
{
|
||||
if (Data != null) return Data;
|
||||
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
if (new FileInfo(FilePath).Length == 0)
|
||||
{
|
||||
Log.Error($"|BinaryStorage.TryLoad|Zero length cache file <{FilePath}>");
|
||||
await SaveAsync(defaultData);
|
||||
return defaultData;
|
||||
Log.Error(ClassName, $"Zero length cache file <{FilePath}>");
|
||||
Data = defaultData;
|
||||
await SaveAsync();
|
||||
}
|
||||
|
||||
await using var stream = new FileStream(FilePath, FileMode.Open);
|
||||
var d = await DeserializeAsync(stream, defaultData);
|
||||
return d;
|
||||
Data = await DeserializeAsync(stream, defaultData);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Info("|BinaryStorage.TryLoad|Cache file not exist, load default data");
|
||||
await SaveAsync(defaultData);
|
||||
return defaultData;
|
||||
Log.Info(ClassName, "Cache file not exist, load default data");
|
||||
Data = defaultData;
|
||||
await SaveAsync();
|
||||
}
|
||||
|
||||
return Data;
|
||||
}
|
||||
|
||||
private static async ValueTask<T> DeserializeAsync(Stream stream, T defaultData)
|
||||
|
|
@ -57,7 +84,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
try
|
||||
{
|
||||
var t = await MemoryPackSerializer.DeserializeAsync<T>(stream);
|
||||
return t;
|
||||
return t ?? defaultData;
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
|
|
@ -66,8 +93,34 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
}
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
// User may delete the directory, so we need to check it
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
var serialized = MemoryPackSerializer.Serialize(Data);
|
||||
File.WriteAllBytes(FilePath, serialized);
|
||||
}
|
||||
|
||||
public async ValueTask SaveAsync()
|
||||
{
|
||||
await SaveAsync(Data.NonNull());
|
||||
}
|
||||
|
||||
// ImageCache need to convert data into concurrent dictionary for usage,
|
||||
// so we would better to clear the data
|
||||
public void ClearData()
|
||||
{
|
||||
Data = default;
|
||||
}
|
||||
|
||||
// ImageCache storages data in its class,
|
||||
// so we need to pass it to SaveAsync
|
||||
public async ValueTask SaveAsync(T data)
|
||||
{
|
||||
// User may delete the directory, so we need to check it
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
await using var stream = new FileStream(FilePath, FileMode.Create);
|
||||
await MemoryPackSerializer.SerializeAsync(stream, data);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,24 @@
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
{
|
||||
public class FlowLauncherJsonStorage<T> : JsonStorage<T> where T : new()
|
||||
// Expose ISaveable interface in derived class to make sure we are calling the new version of Save method
|
||||
public class FlowLauncherJsonStorage<T> : JsonStorage<T>, ISavable where T : new()
|
||||
{
|
||||
private static readonly string ClassName = "FlowLauncherJsonStorage";
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
public FlowLauncherJsonStorage()
|
||||
{
|
||||
var directoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
|
||||
FilesFolders.ValidateDirectory(directoryPath);
|
||||
DirectoryPath = Path.Combine(DataLocation.DataDirectory(), DirectoryName);
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
var filename = typeof(T).Name;
|
||||
FilePath = Path.Combine(directoryPath, $"{filename}{FileSuffix}");
|
||||
FilePath = Path.Combine(DirectoryPath, $"{filename}{FileSuffix}");
|
||||
}
|
||||
|
||||
public new void Save()
|
||||
|
|
@ -32,7 +29,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
|
||||
Log.Exception(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,7 +41,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
|
||||
Log.Exception(ClassName, $"Failed to save FL settings to path: {FilePath}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
{
|
||||
/// <summary>
|
||||
/// Serialize object using json format.
|
||||
/// </summary>
|
||||
public class JsonStorage<T> where T : new()
|
||||
public class JsonStorage<T> : ISavable where T : new()
|
||||
{
|
||||
private static readonly string ClassName = "JsonStorage";
|
||||
|
||||
protected T? Data;
|
||||
|
||||
// need a new directory name
|
||||
|
|
@ -41,6 +45,22 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
}
|
||||
|
||||
public bool Exists()
|
||||
{
|
||||
return File.Exists(FilePath);
|
||||
}
|
||||
|
||||
public void Delete()
|
||||
{
|
||||
foreach (var path in new[] { FilePath, BackupFilePath, TempFilePath })
|
||||
{
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<T> LoadAsync()
|
||||
{
|
||||
if (Data != null)
|
||||
|
|
@ -102,7 +122,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
private void RestoreBackup()
|
||||
{
|
||||
Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully");
|
||||
Log.Info(ClassName, $"Failed to load settings.json, {BackupFilePath} restored successfully");
|
||||
|
||||
if (File.Exists(FilePath))
|
||||
File.Replace(BackupFilePath, FilePath, null);
|
||||
|
|
@ -181,7 +201,10 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
public void Save()
|
||||
{
|
||||
string serialized = JsonSerializer.Serialize(Data,
|
||||
// User may delete the directory, so we need to check it
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
var serialized = JsonSerializer.Serialize(Data,
|
||||
new JsonSerializerOptions { WriteIndented = true });
|
||||
|
||||
File.WriteAllText(TempFilePath, serialized);
|
||||
|
|
@ -191,6 +214,9 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
public async Task SaveAsync()
|
||||
{
|
||||
// User may delete the directory, so we need to check it
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
await using var tempOutput = File.OpenWrite(TempFilePath);
|
||||
await JsonSerializer.SerializeAsync(tempOutput, Data,
|
||||
new JsonSerializerOptions { WriteIndented = true });
|
||||
|
|
|
|||
46
Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs
Normal file
46
Flow.Launcher.Infrastructure/Storage/PluginBinaryStorage.cs
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
{
|
||||
// Expose ISaveable interface in derived class to make sure we are calling the new version of Save method
|
||||
public class PluginBinaryStorage<T> : BinaryStorage<T>, ISavable where T : new()
|
||||
{
|
||||
private static readonly string ClassName = "PluginBinaryStorage";
|
||||
|
||||
public PluginBinaryStorage(string cacheName, string cacheDirectory)
|
||||
{
|
||||
DirectoryPath = cacheDirectory;
|
||||
FilesFolders.ValidateDirectory(DirectoryPath);
|
||||
|
||||
FilePath = Path.Combine(DirectoryPath, $"{cacheName}{FileSuffix}");
|
||||
}
|
||||
|
||||
public new void Save()
|
||||
{
|
||||
try
|
||||
{
|
||||
base.Save();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Log.Exception(ClassName, $"Failed to save plugin caches to path: {FilePath}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public new async Task SaveAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await base.SaveAsync();
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Log.Exception(ClassName, $"Failed to save plugin caches to path: {FilePath}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,20 @@
|
|||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Storage
|
||||
{
|
||||
public class PluginJsonStorage<T> : JsonStorage<T> where T : new()
|
||||
// Expose ISaveable interface in derived class to make sure we are calling the new version of Save method
|
||||
public class PluginJsonStorage<T> : JsonStorage<T>, ISavable where T : new()
|
||||
{
|
||||
// Use assembly name to check which plugin is using this storage
|
||||
public readonly string AssemblyName;
|
||||
|
||||
private static readonly string ClassName = "PluginJsonStorage";
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
public PluginJsonStorage()
|
||||
{
|
||||
// C# related, add python related below
|
||||
|
|
@ -42,7 +39,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
|
||||
Log.Exception(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +51,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
API.LogException(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
|
||||
Log.Exception(ClassName, $"Failed to save plugin settings to path: {FilePath}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using System.Windows.Markup;
|
|||
|
||||
namespace Flow.Launcher.Infrastructure.UI
|
||||
{
|
||||
[Obsolete("EnumBindingSourceExtension is obsolete. Use with Flow.Launcher.Localization NuGet package instead.")]
|
||||
public class EnumBindingSourceExtension : MarkupExtension
|
||||
{
|
||||
private Type _enumType;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,17 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
#region Base
|
||||
|
||||
public abstract class ShortcutBaseModel
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Func<string> Expand { get; set; } = () => { return ""; };
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is ShortcutBaseModel other &&
|
||||
|
|
@ -22,16 +24,14 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
|
||||
public class CustomShortcutModel : ShortcutBaseModel
|
||||
public class BaseCustomShortcutModel : ShortcutBaseModel
|
||||
{
|
||||
public string Value { get; set; }
|
||||
|
||||
[JsonConstructorAttribute]
|
||||
public CustomShortcutModel(string key, string value)
|
||||
public BaseCustomShortcutModel(string key, string value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
Expand = () => { return Value; };
|
||||
}
|
||||
|
||||
public void Deconstruct(out string key, out string value)
|
||||
|
|
@ -40,26 +40,75 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
value = Value;
|
||||
}
|
||||
|
||||
public static implicit operator (string Key, string Value)(CustomShortcutModel shortcut)
|
||||
public static implicit operator (string Key, string Value)(BaseCustomShortcutModel shortcut)
|
||||
{
|
||||
return (shortcut.Key, shortcut.Value);
|
||||
}
|
||||
|
||||
public static implicit operator CustomShortcutModel((string Key, string Value) shortcut)
|
||||
public static implicit operator BaseCustomShortcutModel((string Key, string Value) shortcut)
|
||||
{
|
||||
return new CustomShortcutModel(shortcut.Key, shortcut.Value);
|
||||
return new BaseCustomShortcutModel(shortcut.Key, shortcut.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public class BuiltinShortcutModel : ShortcutBaseModel
|
||||
public class BaseBuiltinShortcutModel : ShortcutBaseModel
|
||||
{
|
||||
public string Description { get; set; }
|
||||
|
||||
public BuiltinShortcutModel(string key, string description, Func<string> expand)
|
||||
public string LocalizedDescription => API.GetTranslation(Description);
|
||||
|
||||
// We should not initialize API in static constructor because it will create another API instance
|
||||
private static IPublicAPI api = null;
|
||||
private static IPublicAPI API => api ??= Ioc.Default.GetRequiredService<IPublicAPI>();
|
||||
|
||||
public BaseBuiltinShortcutModel(string key, string description)
|
||||
{
|
||||
Key = key;
|
||||
Description = description;
|
||||
Expand = expand ?? (() => { return ""; });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Custom Shortcut
|
||||
|
||||
public class CustomShortcutModel : BaseCustomShortcutModel
|
||||
{
|
||||
[JsonIgnore]
|
||||
public Func<string> Expand { get; set; } = () => { return string.Empty; };
|
||||
|
||||
[JsonConstructor]
|
||||
public CustomShortcutModel(string key, string value) : base(key, value)
|
||||
{
|
||||
Expand = () => { return Value; };
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Builtin Shortcut
|
||||
|
||||
public class BuiltinShortcutModel : BaseBuiltinShortcutModel
|
||||
{
|
||||
[JsonIgnore]
|
||||
public Func<string> Expand { get; set; } = () => { return string.Empty; };
|
||||
|
||||
public BuiltinShortcutModel(string key, string description, Func<string> expand) : base(key, description)
|
||||
{
|
||||
Expand = expand ?? (() => { return string.Empty; });
|
||||
}
|
||||
}
|
||||
|
||||
public class AsyncBuiltinShortcutModel : BaseBuiltinShortcutModel
|
||||
{
|
||||
[JsonIgnore]
|
||||
public Func<Task<string>> ExpandAsync { get; set; } = () => { return Task.FromResult(string.Empty); };
|
||||
|
||||
public AsyncBuiltinShortcutModel(string key, string description, Func<Task<string>> expandAsync) : base(key, description)
|
||||
{
|
||||
ExpandAsync = expandAsync ?? (() => { return Task.FromResult(string.Empty); });
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
metadata.Disabled = settings.Disabled;
|
||||
metadata.Priority = settings.Priority;
|
||||
metadata.SearchDelayTime = settings.SearchDelayTime;
|
||||
metadata.HomeDisabled = settings.HomeDisabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -79,6 +80,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
DefaultActionKeywords = metadata.ActionKeywords, // metadata provides default values
|
||||
ActionKeywords = metadata.ActionKeywords, // use default value
|
||||
Disabled = metadata.Disabled,
|
||||
HomeDisabled = metadata.HomeDisabled,
|
||||
Priority = metadata.Priority,
|
||||
DefaultSearchDelayTime = metadata.SearchDelayTime, // metadata provides default values
|
||||
SearchDelayTime = metadata.SearchDelayTime, // use default value
|
||||
|
|
@ -120,14 +122,14 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public int Priority { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public SearchDelayTime? DefaultSearchDelayTime { get; set; }
|
||||
public int? DefaultSearchDelayTime { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchDelayTime? SearchDelayTime { get; set; }
|
||||
public int? SearchDelayTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used only to save the state of the plugin in settings
|
||||
/// </summary>
|
||||
public bool Disabled { get; set; }
|
||||
public bool HomeDisabled { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
|
|
@ -33,8 +33,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
_storage.Save();
|
||||
}
|
||||
|
||||
private string language = Constant.SystemLanguageCode;
|
||||
private string _theme = Constant.DefaultTheme;
|
||||
public string Hotkey { get; set; } = $"{KeyConstant.Alt} + {KeyConstant.Space}";
|
||||
public string OpenResultModifiers { get; set; } = KeyConstant.Alt;
|
||||
public string ColorScheme { get; set; } = "System";
|
||||
|
|
@ -51,18 +49,21 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public string SelectPrevPageHotkey { get; set; } = $"PageDown";
|
||||
public string OpenContextMenuHotkey { get; set; } = $"Ctrl+O";
|
||||
public string SettingWindowHotkey { get; set; } = $"Ctrl+I";
|
||||
public string OpenHistoryHotkey { get; set; } = $"Ctrl+H";
|
||||
public string CycleHistoryUpHotkey { get; set; } = $"{KeyConstant.Alt} + Up";
|
||||
public string CycleHistoryDownHotkey { get; set; } = $"{KeyConstant.Alt} + Down";
|
||||
|
||||
private string _language = Constant.SystemLanguageCode;
|
||||
public string Language
|
||||
{
|
||||
get => language;
|
||||
get => _language;
|
||||
set
|
||||
{
|
||||
language = value;
|
||||
_language = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
private string _theme = Constant.DefaultTheme;
|
||||
public string Theme
|
||||
{
|
||||
get => _theme;
|
||||
|
|
@ -78,22 +79,23 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
public bool UseDropShadowEffect { get; set; } = true;
|
||||
public BackdropTypes BackdropType{ get; set; } = BackdropTypes.None;
|
||||
public string ReleaseNotesVersion { get; set; } = string.Empty;
|
||||
|
||||
/* Appearance Settings. It should be separated from the setting later.*/
|
||||
public double WindowHeightSize { get; set; } = 42;
|
||||
public double ItemHeightSize { get; set; } = 58;
|
||||
public double QueryBoxFontSize { get; set; } = 20;
|
||||
public double QueryBoxFontSize { get; set; } = 16;
|
||||
public double ResultItemFontSize { get; set; } = 16;
|
||||
public double ResultSubItemFontSize { get; set; } = 13;
|
||||
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;
|
||||
public string QueryBoxFont { get; set; } = Win32Helper.GetSystemDefaultFont();
|
||||
public string QueryBoxFontStyle { get; set; }
|
||||
public string QueryBoxFontWeight { get; set; }
|
||||
public string QueryBoxFontStretch { get; set; }
|
||||
public string ResultFont { get; set; } = FontFamily.GenericSansSerif.Name;
|
||||
public string ResultFont { get; set; } = Win32Helper.GetSystemDefaultFont();
|
||||
public string ResultFontStyle { get; set; }
|
||||
public string ResultFontWeight { get; set; }
|
||||
public string ResultFontStretch { get; set; }
|
||||
public string ResultSubFont { get; set; } = FontFamily.GenericSansSerif.Name;
|
||||
public string ResultSubFont { get; set; } = Win32Helper.GetSystemDefaultFont();
|
||||
public string ResultSubFontStyle { get; set; }
|
||||
public string ResultSubFontWeight { get; set; }
|
||||
public string ResultSubFontStretch { get; set; }
|
||||
|
|
@ -101,6 +103,24 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool UseAnimation { get; set; } = true;
|
||||
public bool UseSound { get; set; } = true;
|
||||
public double SoundVolume { get; set; } = 50;
|
||||
public bool ShowBadges { get; set; } = false;
|
||||
public bool ShowBadgesGlobalOnly { get; set; } = false;
|
||||
|
||||
private string _settingWindowFont { get; set; } = Win32Helper.GetSystemDefaultFont(false);
|
||||
public string SettingWindowFont
|
||||
{
|
||||
get => _settingWindowFont;
|
||||
set
|
||||
{
|
||||
if (_settingWindowFont != value)
|
||||
{
|
||||
_settingWindowFont = value;
|
||||
OnPropertyChanged();
|
||||
Application.Current.Resources["SettingWindowFont"] = new FontFamily(value);
|
||||
Application.Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseClock { get; set; } = true;
|
||||
public bool UseDate { get; set; } = false;
|
||||
|
|
@ -116,7 +136,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public bool PrereleaseUpdateSource { get; set; }
|
||||
|
||||
bool _showPlaceholder { get; set; } = false;
|
||||
private bool _showPlaceholder { get; set; } = true;
|
||||
public bool ShowPlaceholder
|
||||
{
|
||||
get => _showPlaceholder;
|
||||
|
|
@ -129,7 +149,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
}
|
||||
string _placeholderText { get; set; } = string.Empty;
|
||||
private string _placeholderText { get; set; } = string.Empty;
|
||||
public string PlaceholderText
|
||||
{
|
||||
get => _placeholderText;
|
||||
|
|
@ -143,6 +163,36 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
|
||||
private bool _showHomePage { get; set; } = true;
|
||||
public bool ShowHomePage
|
||||
{
|
||||
get => _showHomePage;
|
||||
set
|
||||
{
|
||||
if (_showHomePage != value)
|
||||
{
|
||||
_showHomePage = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool _showHistoryResultsForHomePage = false;
|
||||
public bool ShowHistoryResultsForHomePage
|
||||
{
|
||||
get => _showHistoryResultsForHomePage;
|
||||
set
|
||||
{
|
||||
if (_showHistoryResultsForHomePage != value)
|
||||
{
|
||||
_showHistoryResultsForHomePage = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int MaxHistoryResultsToShowForHomePage { get; set; } = 5;
|
||||
|
||||
public int CustomExplorerIndex { get; set; } = 0;
|
||||
|
||||
[JsonIgnore]
|
||||
|
|
@ -180,8 +230,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
new()
|
||||
{
|
||||
Name = "Files",
|
||||
Path = "Files",
|
||||
DirectoryArgument = "-select \"%d\"",
|
||||
Path = "Files-Stable",
|
||||
DirectoryArgument = "\"%d\"",
|
||||
FileArgument = "-select \"%f\""
|
||||
}
|
||||
};
|
||||
|
|
@ -260,6 +310,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public double WindowLeft { get; set; }
|
||||
public double WindowTop { get; set; }
|
||||
public double PreviousScreenWidth { get; set; }
|
||||
public double PreviousScreenHeight { get; set; }
|
||||
public double PreviousDpiX { get; set; }
|
||||
public double PreviousDpiY { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom left position on selected monitor
|
||||
|
|
@ -297,9 +351,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public ObservableCollection<CustomShortcutModel> CustomShortcuts { get; set; } = new ObservableCollection<CustomShortcutModel>();
|
||||
|
||||
[JsonIgnore]
|
||||
public ObservableCollection<BuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
|
||||
public ObservableCollection<BaseBuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
|
||||
{
|
||||
new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
|
||||
new AsyncBuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", () => Win32Helper.StartSTATaskAsync(Clipboard.GetText)),
|
||||
new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath)
|
||||
};
|
||||
|
||||
|
|
@ -309,7 +363,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool StartFlowLauncherOnSystemStartup { get; set; } = false;
|
||||
public bool UseLogonTaskForStartup { get; set; } = false;
|
||||
public bool HideOnStartup { get; set; } = true;
|
||||
bool _hideNotifyIcon { get; set; }
|
||||
private bool _hideNotifyIcon;
|
||||
public bool HideNotifyIcon
|
||||
{
|
||||
get => _hideNotifyIcon;
|
||||
|
|
@ -323,9 +377,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool HideWhenDeactivated { get; set; } = true;
|
||||
|
||||
public bool SearchQueryResultsWithDelay { get; set; }
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchDelayTime SearchDelayTime { get; set; } = SearchDelayTime.Normal;
|
||||
public int SearchDelayTime { get; set; } = 150;
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor;
|
||||
|
|
@ -360,29 +412,31 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
var list = FixedHotkeys();
|
||||
|
||||
// Customizeable hotkeys
|
||||
if(!string.IsNullOrEmpty(Hotkey))
|
||||
if (!string.IsNullOrEmpty(Hotkey))
|
||||
list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = ""));
|
||||
if(!string.IsNullOrEmpty(PreviewHotkey))
|
||||
if (!string.IsNullOrEmpty(PreviewHotkey))
|
||||
list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(AutoCompleteHotkey))
|
||||
if (!string.IsNullOrEmpty(AutoCompleteHotkey))
|
||||
list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(AutoCompleteHotkey2))
|
||||
if (!string.IsNullOrEmpty(AutoCompleteHotkey2))
|
||||
list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = ""));
|
||||
if(!string.IsNullOrEmpty(SelectNextItemHotkey))
|
||||
if (!string.IsNullOrEmpty(SelectNextItemHotkey))
|
||||
list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(SelectNextItemHotkey2))
|
||||
if (!string.IsNullOrEmpty(SelectNextItemHotkey2))
|
||||
list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = ""));
|
||||
if(!string.IsNullOrEmpty(SelectPrevItemHotkey))
|
||||
if (!string.IsNullOrEmpty(SelectPrevItemHotkey))
|
||||
list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(SelectPrevItemHotkey2))
|
||||
if (!string.IsNullOrEmpty(SelectPrevItemHotkey2))
|
||||
list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = ""));
|
||||
if(!string.IsNullOrEmpty(SettingWindowHotkey))
|
||||
if (!string.IsNullOrEmpty(SettingWindowHotkey))
|
||||
list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(OpenContextMenuHotkey))
|
||||
if (!string.IsNullOrEmpty(OpenHistoryHotkey))
|
||||
list.Add(new(OpenHistoryHotkey, "OpenHistoryHotkey", () => OpenHistoryHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(OpenContextMenuHotkey))
|
||||
list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(SelectNextPageHotkey))
|
||||
if (!string.IsNullOrEmpty(SelectNextPageHotkey))
|
||||
list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = ""));
|
||||
if(!string.IsNullOrEmpty(SelectPrevPageHotkey))
|
||||
if (!string.IsNullOrEmpty(SelectPrevPageHotkey))
|
||||
list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = ""));
|
||||
if (!string.IsNullOrEmpty(CycleHistoryUpHotkey))
|
||||
list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = ""));
|
||||
|
|
@ -413,7 +467,6 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
new("Alt+Home", "HotkeySelectFirstResult"),
|
||||
new("Alt+End", "HotkeySelectLastResult"),
|
||||
new("Ctrl+R", "HotkeyRequery"),
|
||||
new("Ctrl+H", "ToggleHistoryHotkey"),
|
||||
new("Ctrl+OemCloseBrackets", "QuickWidthHotkey"),
|
||||
new("Ctrl+OemOpenBrackets", "QuickWidthHotkey"),
|
||||
new("Ctrl+OemPlus", "QuickHeightHotkey"),
|
||||
|
|
|
|||
|
|
@ -1,9 +1,16 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Interop;
|
||||
using System.Windows.Markup;
|
||||
using System.Windows.Media;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Microsoft.Win32;
|
||||
|
|
@ -11,8 +18,10 @@ using Windows.Win32;
|
|||
using Windows.Win32.Foundation;
|
||||
using Windows.Win32.Graphics.Dwm;
|
||||
using Windows.Win32.UI.Input.KeyboardAndMouse;
|
||||
using Windows.Win32.UI.Shell.Common;
|
||||
using Windows.Win32.UI.WindowsAndMessaging;
|
||||
using Point = System.Windows.Point;
|
||||
using SystemFonts = System.Windows.SystemFonts;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
|
|
@ -185,9 +194,9 @@ namespace Flow.Launcher.Infrastructure
|
|||
SetWindowStyle(hwnd, WINDOW_LONG_PTR_INDEX.GWL_STYLE, style);
|
||||
}
|
||||
|
||||
private static int GetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
|
||||
private static nint GetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex)
|
||||
{
|
||||
var style = PInvoke.GetWindowLong(hWnd, nIndex);
|
||||
var style = PInvoke.GetWindowLongPtr(hWnd, nIndex);
|
||||
if (style == 0 && Marshal.GetLastPInvokeError() != 0)
|
||||
{
|
||||
throw new Win32Exception(Marshal.GetLastPInvokeError());
|
||||
|
|
@ -195,7 +204,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
return style;
|
||||
}
|
||||
|
||||
private static nint SetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, int dwNewLong)
|
||||
private static nint SetWindowStyle(HWND hWnd, WINDOW_LONG_PTR_INDEX nIndex, nint dwNewLong)
|
||||
{
|
||||
PInvoke.SetLastError(WIN32_ERROR.NO_ERROR); // Clear any existing error
|
||||
|
||||
|
|
@ -315,6 +324,11 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
public const int WM_ENTERSIZEMOVE = (int)PInvoke.WM_ENTERSIZEMOVE;
|
||||
public const int WM_EXITSIZEMOVE = (int)PInvoke.WM_EXITSIZEMOVE;
|
||||
public const int WM_NCLBUTTONDBLCLK = (int)PInvoke.WM_NCLBUTTONDBLCLK;
|
||||
public const int WM_SYSCOMMAND = (int)PInvoke.WM_SYSCOMMAND;
|
||||
|
||||
public const int SC_MAXIMIZE = (int)PInvoke.SC_MAXIMIZE;
|
||||
public const int SC_MINIMIZE = (int)PInvoke.SC_MINIMIZE;
|
||||
|
||||
#endregion
|
||||
|
||||
|
|
@ -332,6 +346,78 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
#endregion
|
||||
|
||||
#region STA Thread
|
||||
|
||||
/*
|
||||
Inspired by https://github.com/files-community/Files code on STA Thread handling.
|
||||
*/
|
||||
|
||||
public static Task StartSTATaskAsync(Action action)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource();
|
||||
Thread thread = new(() =>
|
||||
{
|
||||
PInvoke.OleInitialize();
|
||||
|
||||
try
|
||||
{
|
||||
action();
|
||||
taskCompletionSource.SetResult();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
taskCompletionSource.SetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PInvoke.OleUninitialize();
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Priority = ThreadPriority.Normal
|
||||
};
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
public static Task<T> StartSTATaskAsync<T>(Func<T> func)
|
||||
{
|
||||
var taskCompletionSource = new TaskCompletionSource<T>();
|
||||
|
||||
Thread thread = new(() =>
|
||||
{
|
||||
PInvoke.OleInitialize();
|
||||
|
||||
try
|
||||
{
|
||||
taskCompletionSource.SetResult(func());
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
taskCompletionSource.SetException(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
PInvoke.OleUninitialize();
|
||||
}
|
||||
})
|
||||
{
|
||||
IsBackground = true,
|
||||
Priority = ThreadPriority.Normal
|
||||
};
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
|
||||
return taskCompletionSource.Task;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Keyboard Layout
|
||||
|
||||
private const string UserProfileRegistryPath = @"Control Panel\International\User Profile";
|
||||
|
|
@ -364,20 +450,10 @@ namespace Flow.Launcher.Infrastructure
|
|||
// No installed English layout found
|
||||
if (enHKL == HKL.Null) return;
|
||||
|
||||
// When application is exiting, the Application.Current will be null
|
||||
if (Application.Current == null) return;
|
||||
|
||||
// Get the FL main window
|
||||
var hwnd = GetWindowHandle(Application.Current.MainWindow, true);
|
||||
// Get the foreground window
|
||||
var hwnd = PInvoke.GetForegroundWindow();
|
||||
if (hwnd == HWND.Null) return;
|
||||
|
||||
// Check if the FL main window is the current foreground window
|
||||
if (!IsForegroundWindow(hwnd))
|
||||
{
|
||||
var result = PInvoke.SetForegroundWindow(hwnd);
|
||||
if (!result) throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
// Get the current foreground window thread ID
|
||||
var threadId = PInvoke.GetWindowThreadProcessId(hwnd);
|
||||
if (threadId == 0) throw new Win32Exception(Marshal.GetLastWin32Error());
|
||||
|
|
@ -517,5 +593,203 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Korean IME
|
||||
|
||||
public static bool IsWindows11()
|
||||
{
|
||||
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
|
||||
Environment.OSVersion.Version.Build >= 22000;
|
||||
}
|
||||
|
||||
public static bool IsKoreanIMEExist()
|
||||
{
|
||||
return GetLegacyKoreanIMERegistryValue() != null;
|
||||
}
|
||||
|
||||
public static bool IsLegacyKoreanIMEEnabled()
|
||||
{
|
||||
object value = GetLegacyKoreanIMERegistryValue();
|
||||
|
||||
if (value is int intValue)
|
||||
{
|
||||
return intValue == 1;
|
||||
}
|
||||
else if (value != null && int.TryParse(value.ToString(), out int parsedValue))
|
||||
{
|
||||
return parsedValue == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool SetLegacyKoreanIMEEnabled(bool enable)
|
||||
{
|
||||
const string subKeyPath = @"Software\Microsoft\input\tsf\tsf3override\{A028AE76-01B1-46C2-99C4-ACD9858AE02F}";
|
||||
const string valueName = "NoTsf3Override5";
|
||||
|
||||
try
|
||||
{
|
||||
using RegistryKey key = Registry.CurrentUser.CreateSubKey(subKeyPath);
|
||||
if (key != null)
|
||||
{
|
||||
int value = enable ? 1 : 0;
|
||||
key.SetValue(valueName, value, RegistryValueKind.DWord);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static object GetLegacyKoreanIMERegistryValue()
|
||||
{
|
||||
const string subKeyPath = @"Software\Microsoft\input\tsf\tsf3override\{A028AE76-01B1-46C2-99C4-ACD9858AE02F}";
|
||||
const string valueName = "NoTsf3Override5";
|
||||
|
||||
try
|
||||
{
|
||||
using RegistryKey key = Registry.CurrentUser.OpenSubKey(subKeyPath);
|
||||
if (key != null)
|
||||
{
|
||||
return key.GetValue(valueName);
|
||||
}
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void OpenImeSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("ms-settings:regionlanguage") { UseShellExecute = true });
|
||||
}
|
||||
catch (System.Exception)
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System Font
|
||||
|
||||
private static readonly Dictionary<string, string> _languageToNotoSans = new()
|
||||
{
|
||||
{ "ko", "Noto Sans KR" },
|
||||
{ "ja", "Noto Sans JP" },
|
||||
{ "zh-CN", "Noto Sans SC" },
|
||||
{ "zh-SG", "Noto Sans SC" },
|
||||
{ "zh-Hans", "Noto Sans SC" },
|
||||
{ "zh-TW", "Noto Sans TC" },
|
||||
{ "zh-HK", "Noto Sans TC" },
|
||||
{ "zh-MO", "Noto Sans TC" },
|
||||
{ "zh-Hant", "Noto Sans TC" },
|
||||
{ "th", "Noto Sans Thai" },
|
||||
{ "ar", "Noto Sans Arabic" },
|
||||
{ "he", "Noto Sans Hebrew" },
|
||||
{ "hi", "Noto Sans Devanagari" },
|
||||
{ "bn", "Noto Sans Bengali" },
|
||||
{ "ta", "Noto Sans Tamil" },
|
||||
{ "el", "Noto Sans Greek" },
|
||||
{ "ru", "Noto Sans" },
|
||||
{ "en", "Noto Sans" },
|
||||
{ "fr", "Noto Sans" },
|
||||
{ "de", "Noto Sans" },
|
||||
{ "es", "Noto Sans" },
|
||||
{ "pt", "Noto Sans" }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the system default font.
|
||||
/// </summary>
|
||||
/// <param name="useNoto">
|
||||
/// If true, it will try to find the Noto font for the current culture.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The name of the system default font.
|
||||
/// </returns>
|
||||
public static string GetSystemDefaultFont(bool useNoto = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (useNoto)
|
||||
{
|
||||
var culture = CultureInfo.CurrentCulture;
|
||||
var language = culture.Name; // e.g., "zh-TW"
|
||||
var langPrefix = language.Split('-')[0]; // e.g., "zh"
|
||||
|
||||
// First, try to find by full name, and if not found, fallback to prefix
|
||||
if (TryGetNotoFont(language, out var notoFont) || TryGetNotoFont(langPrefix, out notoFont))
|
||||
{
|
||||
// If the font is installed, return it
|
||||
if (Fonts.SystemFontFamilies.Any(f => f.Source.Equals(notoFont)))
|
||||
{
|
||||
return notoFont;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If Noto font is not found, fallback to the system default font
|
||||
var font = SystemFonts.MessageFontFamily;
|
||||
if (font.FamilyNames.TryGetValue(XmlLanguage.GetLanguage("en-US"), out var englishName))
|
||||
{
|
||||
return englishName;
|
||||
}
|
||||
|
||||
return font.Source ?? "Segoe UI";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Segoe UI";
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryGetNotoFont(string langKey, out string notoFont)
|
||||
{
|
||||
return _languageToNotoSans.TryGetValue(langKey, out notoFont);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Explorer
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shopenfolderandselectitems
|
||||
|
||||
public static unsafe void OpenFolderAndSelectFile(string filePath)
|
||||
{
|
||||
ITEMIDLIST* pidlFolder = null;
|
||||
ITEMIDLIST* pidlFile = null;
|
||||
|
||||
var folderPath = Path.GetDirectoryName(filePath);
|
||||
|
||||
try
|
||||
{
|
||||
var hrFolder = PInvoke.SHParseDisplayName(folderPath, null, out pidlFolder, 0, null);
|
||||
if (hrFolder.Failed) throw new COMException("Failed to parse folder path", hrFolder);
|
||||
|
||||
var hrFile = PInvoke.SHParseDisplayName(filePath, null, out pidlFile, 0, null);
|
||||
if (hrFile.Failed) throw new COMException("Failed to parse file path", hrFile);
|
||||
|
||||
var hrSelect = PInvoke.SHOpenFolderAndSelectItems(pidlFolder, 1, &pidlFile, 0);
|
||||
if (hrSelect.Failed) throw new COMException("Failed to open folder and select item", hrSelect);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (pidlFile != null) PInvoke.CoTaskMemFree(pidlFile);
|
||||
if (pidlFolder != null) PInvoke.CoTaskMemFree(pidlFolder);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,42 @@ namespace Flow.Launcher.Plugin
|
|||
public static bool IsDotNet(string language)
|
||||
{
|
||||
return language.Equals(CSharp, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(FSharp, StringComparison.OrdinalIgnoreCase);
|
||||
|| language.Equals(FSharp, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this language is a Python language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsPython(string language)
|
||||
{
|
||||
return language.Equals(Python, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(PythonV2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this language is a Node.js language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsNodeJs(string language)
|
||||
{
|
||||
return language.Equals(TypeScript, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(TypeScriptV2, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(JavaScript, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(JavaScriptV2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this language is a executable language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsExecutable(string language)
|
||||
{
|
||||
return language.Equals(Executable, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(ExecutableV2, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -76,15 +111,9 @@ namespace Flow.Launcher.Plugin
|
|||
public static bool IsAllowed(string language)
|
||||
{
|
||||
return IsDotNet(language)
|
||||
|| language.Equals(Python, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(PythonV2, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(Executable, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(TypeScript, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(JavaScript, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(ExecutableV2, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(TypeScriptV2, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(JavaScriptV2, StringComparison.OrdinalIgnoreCase);
|
||||
;
|
||||
|| IsPython(language)
|
||||
|| IsNodeJs(language)
|
||||
|| IsExecutable(language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>4.4.0</Version>
|
||||
<PackageVersion>4.4.0</PackageVersion>
|
||||
<AssemblyVersion>4.4.0</AssemblyVersion>
|
||||
<FileVersion>4.4.0</FileVersion>
|
||||
<Version>4.5.0</Version>
|
||||
<PackageVersion>4.5.0</PackageVersion>
|
||||
<AssemblyVersion>4.5.0</AssemblyVersion>
|
||||
<FileVersion>4.5.0</FileVersion>
|
||||
<PackageId>Flow.Launcher.Plugin</PackageId>
|
||||
<Authors>Flow-Launcher</Authors>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
|
@ -76,7 +76,9 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
23
Flow.Launcher.Plugin/Interfaces/IAsyncHomeQuery.cs
Normal file
23
Flow.Launcher.Plugin/Interfaces/IAsyncHomeQuery.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronous Query Model for Flow Launcher When Query Text is Empty
|
||||
/// </summary>
|
||||
public interface IAsyncHomeQuery : IFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Asynchronous Querying When Query Text is Empty
|
||||
/// </summary>
|
||||
/// <para>
|
||||
/// If the Querying method requires high IO transmission
|
||||
/// or performing CPU intense jobs (performing better with cancellation), please use this IAsyncHomeQuery interface
|
||||
/// </para>
|
||||
/// <param name="token">Cancel when querying job is obsolete</param>
|
||||
/// <returns></returns>
|
||||
Task<List<Result>> HomeQueryAsync(CancellationToken token);
|
||||
}
|
||||
}
|
||||
28
Flow.Launcher.Plugin/Interfaces/IHomeQuery.cs
Normal file
28
Flow.Launcher.Plugin/Interfaces/IHomeQuery.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Synchronous Query Model for Flow Launcher When Query Text is Empty
|
||||
/// <para>
|
||||
/// If the Querying method requires high IO transmission
|
||||
/// or performing CPU intense jobs (performing better with cancellation), please try the IAsyncHomeQuery interface
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public interface IHomeQuery : IAsyncHomeQuery
|
||||
{
|
||||
/// <summary>
|
||||
/// Querying When Query Text is Empty
|
||||
/// <para>
|
||||
/// This method will be called within a Task.Run,
|
||||
/// so please avoid synchronously wait for long.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
List<Result> HomeQuery();
|
||||
|
||||
Task<List<Result>> IAsyncHomeQuery.HomeQueryAsync(CancellationToken token) => Task.Run(HomeQuery);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using JetBrains.Annotations;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
|
|
@ -8,6 +6,9 @@ using System.Runtime.CompilerServices;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
|
|
@ -83,10 +84,24 @@ namespace Flow.Launcher.Plugin
|
|||
/// <param name="subTitle">Optional message subtitle</param>
|
||||
void ShowMsgError(string title, string subTitle = "");
|
||||
|
||||
/// <summary>
|
||||
/// Show the error message using Flow's standard error icon.
|
||||
/// </summary>
|
||||
/// <param name="title">Message title</param>
|
||||
/// <param name="buttonText">Message button content</param>
|
||||
/// <param name="buttonAction">Message button action</param>
|
||||
/// <param name="subTitle">Optional message subtitle</param>
|
||||
void ShowMsgErrorWithButton(string title, string buttonText, Action buttonAction, string subTitle = "");
|
||||
|
||||
/// <summary>
|
||||
/// Show the MainWindow when hiding
|
||||
/// </summary>
|
||||
void ShowMainWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Focus the query text box in the main window
|
||||
/// </summary>
|
||||
void FocusQueryTextBox();
|
||||
|
||||
/// <summary>
|
||||
/// Hide MainWindow
|
||||
|
|
@ -121,6 +136,27 @@ namespace Flow.Launcher.Plugin
|
|||
/// <param name="useMainWindowAsOwner">when true will use main windows as the owner</param>
|
||||
void ShowMsg(string title, string subTitle, string iconPath, bool useMainWindowAsOwner = true);
|
||||
|
||||
/// <summary>
|
||||
/// Show message box with button
|
||||
/// </summary>
|
||||
/// <param name="title">Message title</param>
|
||||
/// <param name="buttonText">Message button content</param>
|
||||
/// <param name="buttonAction">Message button action</param>
|
||||
/// <param name="subTitle">Message subtitle</param>
|
||||
/// <param name="iconPath">Message icon path (relative path to your plugin folder)</param>
|
||||
void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle = "", string iconPath = "");
|
||||
|
||||
/// <summary>
|
||||
/// Show message box with button
|
||||
/// </summary>
|
||||
/// <param name="title">Message title</param>
|
||||
/// <param name="buttonText">Message button content</param>
|
||||
/// <param name="buttonAction">Message button action</param>
|
||||
/// <param name="subTitle">Message subtitle</param>
|
||||
/// <param name="iconPath">Message icon path (relative path to your plugin folder)</param>
|
||||
/// <param name="useMainWindowAsOwner">when true will use main windows as the owner</param>
|
||||
void ShowMsgWithButton(string title, string buttonText, Action buttonAction, string subTitle, string iconPath, bool useMainWindowAsOwner = true);
|
||||
|
||||
/// <summary>
|
||||
/// Open setting dialog
|
||||
/// </summary>
|
||||
|
|
@ -141,15 +177,47 @@ namespace Flow.Launcher.Plugin
|
|||
List<PluginPair> GetAllPlugins();
|
||||
|
||||
/// <summary>
|
||||
/// Register a callback for Global Keyboard Event
|
||||
/// Registers a callback function for global keyboard events.
|
||||
/// </summary>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="callback">
|
||||
/// The callback function to invoke when a global keyboard event occurs.
|
||||
/// <para>
|
||||
/// Parameters:
|
||||
/// <list type="number">
|
||||
/// <item><description>int: The type of <see cref="KeyEvent"/> (key down, key up, etc.)</description></item>
|
||||
/// <item><description>int: The virtual key code of the pressed/released key</description></item>
|
||||
/// <item><description><see cref="SpecialKeyState"/>: The state of modifier keys (Ctrl, Alt, Shift, etc.)</description></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Returns: <c>true</c> to allow normal system processing of the key event,
|
||||
/// or <c>false</c> to intercept and prevent default handling.
|
||||
/// </para>
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// This callback will be invoked for all keyboard events system-wide.
|
||||
/// Use with caution as intercepting system keys may affect normal system operation.
|
||||
/// </remarks>
|
||||
public void RegisterGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Remove a callback for Global Keyboard Event
|
||||
/// </summary>
|
||||
/// <param name="callback"></param>
|
||||
/// <param name="callback">
|
||||
/// The callback function to invoke when a global keyboard event occurs.
|
||||
/// <para>
|
||||
/// Parameters:
|
||||
/// <list type="number">
|
||||
/// <item><description>int: The type of <see cref="KeyEvent"/> (key down, key up, etc.)</description></item>
|
||||
/// <item><description>int: The virtual key code of the pressed/released key</description></item>
|
||||
/// <item><description><see cref="SpecialKeyState"/>: The state of modifier keys (Ctrl, Alt, Shift, etc.)</description></item>
|
||||
/// </list>
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Returns: <c>true</c> to allow normal system processing of the key event,
|
||||
/// or <c>false</c> to intercept and prevent default handling.
|
||||
/// </para>
|
||||
/// </param>
|
||||
public void RemoveGlobalKeyboardCallback(Func<int, int, SpecialKeyState, bool> callback);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -190,11 +258,15 @@ namespace Flow.Launcher.Plugin
|
|||
Task HttpDownloadAsync([NotNull] string url, [NotNull] string filePath, Action<double> reportProgress = null, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Add ActionKeyword and update action keyword metadata for specific plugin
|
||||
/// Add ActionKeyword and update action keyword metadata for specific plugin.
|
||||
/// Before adding, please check if action keyword is already assigned by <see cref="ActionKeywordAssigned"/>
|
||||
/// </summary>
|
||||
/// <param name="pluginId">ID for plugin that needs to add action keyword</param>
|
||||
/// <param name="newActionKeyword">The actionkeyword that is supposed to be added</param>
|
||||
/// <remarks>
|
||||
/// If new action keyword contains any whitespace, FL will still add it but it will not work for users.
|
||||
/// So plugin should check the whitespace before calling this function.
|
||||
/// </remarks>
|
||||
void AddActionKeyword(string pluginId, string newActionKeyword);
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -227,6 +299,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
void LogWarn(string className, string message, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log error message. Preferred error logging method for plugins.
|
||||
/// </summary>
|
||||
void LogError(string className, string message, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log an Exception. Will throw if in debug mode so developer will be aware,
|
||||
/// otherwise logs the eror message. This is the primary logging method used for Flow
|
||||
|
|
@ -242,9 +319,10 @@ namespace Flow.Launcher.Plugin
|
|||
T LoadSettingJsonStorage<T>() where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// Save JsonStorage for current plugin's setting. This is the method used to save settings to json in Flow.Launcher
|
||||
/// Save JsonStorage for current plugin's setting. This is the method used to save settings to json in Flow.
|
||||
/// This method will save the original instance loaded with LoadJsonStorage.
|
||||
/// This API call is for manually Save. Flow will automatically save all setting type that has called LoadSettingJsonStorage or SaveSettingJsonStorage previously.
|
||||
/// This API call is for manually Save.
|
||||
/// Flow will automatically save all setting type that has called <see cref="LoadSettingJsonStorage"/> or <see cref="SaveSettingJsonStorage"/> previously.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type for Serialization</typeparam>
|
||||
/// <returns></returns>
|
||||
|
|
@ -345,6 +423,78 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public void StopLoadingBar();
|
||||
|
||||
/// <summary>
|
||||
/// Get all available themes
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<ThemeData> GetAvailableThemes();
|
||||
|
||||
/// <summary>
|
||||
/// Get the current theme
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public ThemeData GetCurrentTheme();
|
||||
|
||||
/// <summary>
|
||||
/// Set the current theme
|
||||
/// </summary>
|
||||
/// <param name="theme"></param>
|
||||
/// <returns>
|
||||
/// True if the theme is set successfully, false otherwise.
|
||||
/// </returns>
|
||||
public bool SetCurrentTheme(ThemeData theme);
|
||||
|
||||
/// <summary>
|
||||
/// Save all Flow's plugins caches
|
||||
/// </summary>
|
||||
void SavePluginCaches();
|
||||
|
||||
/// <summary>
|
||||
/// Load BinaryStorage for current plugin's cache. This is the method used to load cache from binary in Flow.
|
||||
/// When the file is not exist, it will create a new instance for the specific type.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type for deserialization</typeparam>
|
||||
/// <param name="cacheName">Cache file name</param>
|
||||
/// <param name="cacheDirectory">Cache directory from plugin metadata</param>
|
||||
/// <param name="defaultData">Default data to return</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// BinaryStorage utilizes MemoryPack, which means the object must be MemoryPackSerializable <see href="https://github.com/Cysharp/MemoryPack"/>
|
||||
/// </remarks>
|
||||
Task<T> LoadCacheBinaryStorageAsync<T>(string cacheName, string cacheDirectory, T defaultData) where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// Save BinaryStorage for current plugin's cache. This is the method used to save cache to binary in Flow.
|
||||
/// This method will save the original instance loaded with LoadCacheBinaryStorageAsync.
|
||||
/// This API call is for manually Save.
|
||||
/// Flow will automatically save all cache type that has called <see cref="LoadCacheBinaryStorageAsync"/> or <see cref="SaveCacheBinaryStorageAsync"/> previously.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type for Serialization</typeparam>
|
||||
/// <param name="cacheName">Cache file name</param>
|
||||
/// <param name="cacheDirectory">Cache directory from plugin metadata</param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// BinaryStorage utilizes MemoryPack, which means the object must be MemoryPackSerializable <see href="https://github.com/Cysharp/MemoryPack"/>
|
||||
/// </remarks>
|
||||
Task SaveCacheBinaryStorageAsync<T>(string cacheName, string cacheDirectory) where T : new();
|
||||
|
||||
/// <summary>
|
||||
/// Load image from path.
|
||||
/// Support local, remote and data:image url.
|
||||
/// Support png, jpg, jpeg, gif, bmp, tiff, ico, svg image files.
|
||||
/// If image path is missing, it will return a missing icon.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the image.</param>
|
||||
/// <param name="loadFullImage">
|
||||
/// Load full image or not.
|
||||
/// </param>
|
||||
/// <param name="cacheImage">
|
||||
/// Cache the image or not. Cached image will be stored in FL cache.
|
||||
/// If the image is just used one time, it's better to set this to false.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
ValueTask<ImageSource> LoadImageAsync(string path, bool loadFullImage = false, bool cacheImage = true);
|
||||
|
||||
/// <summary>
|
||||
/// Update the plugin manifest
|
||||
/// </summary>
|
||||
|
|
@ -356,8 +506,11 @@ namespace Flow.Launcher.Plugin
|
|||
public Task<bool> UpdatePluginManifestAsync(bool usePrimaryUrlOnly = false, CancellationToken token = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the plugin manifest
|
||||
/// Get the plugin manifest.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If Flow cannot get manifest data, this could be null
|
||||
/// </remarks>
|
||||
/// <returns></returns>
|
||||
public IReadOnlyList<UserPlugin> GetPluginManifest();
|
||||
|
||||
|
|
@ -401,5 +554,31 @@ namespace Flow.Launcher.Plugin
|
|||
/// </param>
|
||||
/// <returns></returns>
|
||||
public Task UninstallPluginAsync(PluginMetadata pluginMetadata, bool removePluginSettings = false);
|
||||
|
||||
/// <summary>
|
||||
/// Log debug message of the time taken to execute a method
|
||||
/// Message will only be logged in Debug mode
|
||||
/// </summary>
|
||||
/// <returns>The time taken to execute the method in milliseconds</returns>
|
||||
public long StopwatchLogDebug(string className, string message, Action action, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log debug message of the time taken to execute a method asynchronously
|
||||
/// Message will only be logged in Debug mode
|
||||
/// </summary>
|
||||
/// <returns>The time taken to execute the method in milliseconds</returns>
|
||||
public Task<long> StopwatchLogDebugAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log info message of the time taken to execute a method
|
||||
/// </summary>
|
||||
/// <returns>The time taken to execute the method in milliseconds</returns>
|
||||
public long StopwatchLogInfo(string className, string message, Action action, [CallerMemberName] string methodName = "");
|
||||
|
||||
/// <summary>
|
||||
/// Log info message of the time taken to execute a method asynchronously
|
||||
/// </summary>
|
||||
/// <returns>The time taken to execute the method in milliseconds</returns>
|
||||
public Task<long> StopwatchLogInfoAsync(string className, string message, Func<Task> action, [CallerMemberName] string methodName = "");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,21 @@
|
|||
namespace Flow.Launcher.Plugin
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Inherit this interface if additional data e.g. cache needs to be saved.
|
||||
/// Inherit this interface if you need to save additional data which is not a setting or cache,
|
||||
/// please implement this interface.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For storing plugin settings, prefer <see cref="IPublicAPI.LoadSettingJsonStorage{T}"/>
|
||||
/// or <see cref="IPublicAPI.SaveSettingJsonStorage{T}"/>.
|
||||
/// Once called, your settings will be automatically saved by Flow.
|
||||
/// or <see cref="IPublicAPI.SaveSettingJsonStorage{T}"/>.
|
||||
/// For storing plugin caches, prefer <see cref="IPublicAPI.LoadCacheBinaryStorageAsync{T}"/>
|
||||
/// or <see cref="IPublicAPI.SaveCacheBinaryStorageAsync{T}(string, string)"/>.
|
||||
/// Once called, those settings and caches will be automatically saved by Flow.
|
||||
/// </remarks>
|
||||
public interface ISavable : IFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Save additional plugin data, such as cache.
|
||||
/// Save additional plugin data.
|
||||
/// </summary>
|
||||
void Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
using Windows.Win32;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Hotkey
|
||||
namespace Flow.Launcher.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumeration of key events for
|
||||
/// <see cref="IPublicAPI.RegisterGlobalKeyboardCallback(System.Func{int, int, SpecialKeyState, bool})"/>
|
||||
/// and <see cref="IPublicAPI.RemoveGlobalKeyboardCallback(System.Func{int, int, SpecialKeyState, bool})"/>
|
||||
/// </summary>
|
||||
public enum KeyEvent
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -1,3 +1,8 @@
|
|||
EnumThreadWindows
|
||||
GetWindowText
|
||||
GetWindowTextLength
|
||||
GetWindowTextLength
|
||||
|
||||
WM_KEYDOWN
|
||||
WM_KEYUP
|
||||
WM_SYSKEYDOWN
|
||||
WM_SYSKEYUP
|
||||
|
|
@ -50,6 +50,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public bool Disabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether plugin is disabled in home query.
|
||||
/// </summary>
|
||||
public bool HomeDisabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plugin execute file path.
|
||||
/// </summary>
|
||||
|
|
@ -99,10 +104,9 @@ namespace Flow.Launcher.Plugin
|
|||
public bool HideActionKeywordPanel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Plugin search delay time. Null means use default search delay time.
|
||||
/// Plugin search delay time in ms. Null means use default search delay time.
|
||||
/// </summary>
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchDelayTime? SearchDelayTime { get; set; } = null;
|
||||
public int? SearchDelayTime { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Plugin icon path.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ namespace Flow.Launcher.Plugin
|
|||
public class Query
|
||||
{
|
||||
/// <summary>
|
||||
/// Raw query, this includes action keyword if it has
|
||||
/// Raw query, this includes action keyword if it has.
|
||||
/// It has handled buildin custom query shortkeys and build-in shortcuts, and it trims the whitespace.
|
||||
/// We didn't recommend use this property directly. You should always use Search property.
|
||||
/// </summary>
|
||||
public string RawQuery { get; internal init; }
|
||||
|
|
@ -20,6 +21,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public bool IsReQuery { get; internal set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the query is a home query.
|
||||
/// </summary>
|
||||
public bool IsHomeQuery { get; internal init; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Search part of a query.
|
||||
/// This will not include action keyword if exclusive plugin gets it, otherwise it should be same as RawQuery.
|
||||
|
|
@ -63,10 +69,10 @@ namespace Flow.Launcher.Plugin
|
|||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public string FirstSearch => SplitSearch(0);
|
||||
|
||||
|
||||
[JsonIgnore]
|
||||
private string _secondToEndSearch;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// strings from second search (including) to last search
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -12,12 +12,19 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public class Result
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum score. This can be useful when set one result to the top by default. This is the score for the results set to the topmost by users.
|
||||
/// </summary>
|
||||
public const int MaxScore = int.MaxValue;
|
||||
|
||||
private string _pluginDirectory;
|
||||
|
||||
private string _icoPath;
|
||||
|
||||
private string _copyText = string.Empty;
|
||||
|
||||
private string _badgeIcoPath;
|
||||
|
||||
/// <summary>
|
||||
/// The title of the result. This is always required.
|
||||
/// </summary>
|
||||
|
|
@ -60,7 +67,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// <remarks>GlyphInfo is prioritized if not null</remarks>
|
||||
public string IcoPath
|
||||
{
|
||||
get { return _icoPath; }
|
||||
get => _icoPath;
|
||||
set
|
||||
{
|
||||
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
|
||||
|
|
@ -80,6 +87,33 @@ namespace Flow.Launcher.Plugin
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The image to be displayed for the badge of the result.
|
||||
/// </summary>
|
||||
/// <value>Can be a local file path or a URL.</value>
|
||||
/// <remarks>If null or empty, will use plugin icon</remarks>
|
||||
public string BadgeIcoPath
|
||||
{
|
||||
get => _badgeIcoPath;
|
||||
set
|
||||
{
|
||||
// As a standard this property will handle prepping and converting to absolute local path for icon image processing
|
||||
if (!string.IsNullOrEmpty(value)
|
||||
&& !string.IsNullOrEmpty(PluginDirectory)
|
||||
&& !Path.IsPathRooted(value)
|
||||
&& !value.StartsWith("http://", StringComparison.OrdinalIgnoreCase)
|
||||
&& !value.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
|
||||
&& !value.StartsWith("data:image", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_badgeIcoPath = Path.Combine(PluginDirectory, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_badgeIcoPath = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if Icon has a border radius
|
||||
/// </summary>
|
||||
|
|
@ -94,14 +128,18 @@ namespace Flow.Launcher.Plugin
|
|||
/// <summary>
|
||||
/// Delegate to load an icon for this result.
|
||||
/// </summary>
|
||||
public IconDelegate Icon;
|
||||
public IconDelegate Icon = null;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate to load an icon for the badge of this result.
|
||||
/// </summary>
|
||||
public IconDelegate BadgeIcon = null;
|
||||
|
||||
/// <summary>
|
||||
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
|
||||
/// </summary>
|
||||
public GlyphInfo Glyph { get; init; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// An action to take in the form of a function call when the result has been selected.
|
||||
/// </summary>
|
||||
|
|
@ -143,59 +181,19 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public string PluginDirectory
|
||||
{
|
||||
get { return _pluginDirectory; }
|
||||
get => _pluginDirectory;
|
||||
set
|
||||
{
|
||||
_pluginDirectory = value;
|
||||
|
||||
// When the Result object is returned from the query call, PluginDirectory is not provided until
|
||||
// UpdatePluginMetadata call is made at PluginManager.cs L196. Once the PluginDirectory becomes available
|
||||
// we need to update (only if not Uri path) the IcoPath with the full absolute path so the image can be loaded.
|
||||
// we need to update (only if not Uri path) the IcoPath and BadgeIcoPath with the full absolute path so the image can be loaded.
|
||||
IcoPath = _icoPath;
|
||||
BadgeIcoPath = _badgeIcoPath;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Title + SubTitle + Score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the current result
|
||||
/// </summary>
|
||||
public Result Clone()
|
||||
{
|
||||
return new Result
|
||||
{
|
||||
Title = Title,
|
||||
SubTitle = SubTitle,
|
||||
ActionKeywordAssigned = ActionKeywordAssigned,
|
||||
CopyText = CopyText,
|
||||
AutoCompleteText = AutoCompleteText,
|
||||
IcoPath = IcoPath,
|
||||
RoundedIcon = RoundedIcon,
|
||||
Icon = Icon,
|
||||
Glyph = Glyph,
|
||||
Action = Action,
|
||||
AsyncAction = AsyncAction,
|
||||
Score = Score,
|
||||
TitleHighlightData = TitleHighlightData,
|
||||
OriginQuery = OriginQuery,
|
||||
PluginDirectory = PluginDirectory,
|
||||
ContextData = ContextData,
|
||||
PluginID = PluginID,
|
||||
TitleToolTip = TitleToolTip,
|
||||
SubTitleToolTip = SubTitleToolTip,
|
||||
PreviewPanel = PreviewPanel,
|
||||
ProgressBar = ProgressBar,
|
||||
ProgressBarColor = ProgressBarColor,
|
||||
Preview = Preview,
|
||||
AddSelectedCount = AddSelectedCount,
|
||||
RecordKey = RecordKey
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional data associated with this result
|
||||
/// </summary>
|
||||
|
|
@ -224,16 +222,6 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public Lazy<UserControl> PreviewPanel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Run this result, asynchronously
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public ValueTask<bool> ExecuteAsync(ActionContext context)
|
||||
{
|
||||
return AsyncAction?.Invoke(context) ?? ValueTask.FromResult(Action?.Invoke(context) ?? false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Progress bar display. Providing an int value between 0-100 will trigger the progress bar to be displayed on the result
|
||||
/// </summary>
|
||||
|
|
@ -255,11 +243,6 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public bool AddSelectedCount { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum score. This can be useful when set one result to the top by default. This is the score for the results set to the topmost by users.
|
||||
/// </summary>
|
||||
public const int MaxScore = int.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// The key to identify the record. This is used when FL checks whether the result is the topmost record. Or FL calculates the hashcode of the result for user selected records.
|
||||
/// This can be useful when your plugin will change the Title or SubTitle of the result dynamically.
|
||||
|
|
@ -268,6 +251,66 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public string RecordKey { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the badge icon should be shown.
|
||||
/// If users want to show the result badges and here you set this to true, the results will show the badge icon.
|
||||
/// </summary>
|
||||
public bool ShowBadge { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Run this result, asynchronously
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public ValueTask<bool> ExecuteAsync(ActionContext context)
|
||||
{
|
||||
return AsyncAction?.Invoke(context) ?? ValueTask.FromResult(Action?.Invoke(context) ?? false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Title + SubTitle + Score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the current result
|
||||
/// </summary>
|
||||
public Result Clone()
|
||||
{
|
||||
return new Result
|
||||
{
|
||||
Title = Title,
|
||||
SubTitle = SubTitle,
|
||||
ActionKeywordAssigned = ActionKeywordAssigned,
|
||||
CopyText = CopyText,
|
||||
AutoCompleteText = AutoCompleteText,
|
||||
IcoPath = IcoPath,
|
||||
BadgeIcoPath = BadgeIcoPath,
|
||||
RoundedIcon = RoundedIcon,
|
||||
Icon = Icon,
|
||||
BadgeIcon = BadgeIcon,
|
||||
Glyph = Glyph,
|
||||
Action = Action,
|
||||
AsyncAction = AsyncAction,
|
||||
Score = Score,
|
||||
TitleHighlightData = TitleHighlightData,
|
||||
OriginQuery = OriginQuery,
|
||||
PluginDirectory = PluginDirectory,
|
||||
ContextData = ContextData,
|
||||
PluginID = PluginID,
|
||||
TitleToolTip = TitleToolTip,
|
||||
SubTitleToolTip = SubTitleToolTip,
|
||||
PreviewPanel = PreviewPanel,
|
||||
ProgressBar = ProgressBar,
|
||||
ProgressBarColor = ProgressBarColor,
|
||||
Preview = Preview,
|
||||
AddSelectedCount = AddSelectedCount,
|
||||
RecordKey = RecordKey,
|
||||
ShowBadge = ShowBadge,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Info of the preview section of a <see cref="Result"/>
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
namespace Flow.Launcher.Plugin;
|
||||
|
||||
/// <summary>
|
||||
/// Enum for search delay time
|
||||
/// </summary>
|
||||
public enum SearchDelayTime
|
||||
{
|
||||
/// <summary>
|
||||
/// Very long search delay time. 250ms.
|
||||
/// </summary>
|
||||
VeryLong,
|
||||
|
||||
/// <summary>
|
||||
/// Long search delay time. 200ms.
|
||||
/// </summary>
|
||||
Long,
|
||||
|
||||
/// <summary>
|
||||
/// Normal search delay time. 150ms. Default value.
|
||||
/// </summary>
|
||||
Normal,
|
||||
|
||||
/// <summary>
|
||||
/// Short search delay time. 100ms.
|
||||
/// </summary>
|
||||
Short,
|
||||
|
||||
/// <summary>
|
||||
/// Very short search delay time. 50ms.
|
||||
/// </summary>
|
||||
VeryShort
|
||||
}
|
||||
|
|
@ -264,12 +264,12 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
var index = path.LastIndexOf('\\');
|
||||
if (index > 0 && index < (path.Length - 1))
|
||||
{
|
||||
string previousDirectoryPath = path.Substring(0, index + 1);
|
||||
return locationExists(previousDirectoryPath) ? previousDirectoryPath : "";
|
||||
string previousDirectoryPath = path[..(index + 1)];
|
||||
return locationExists(previousDirectoryPath) ? previousDirectoryPath : string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,7 +285,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
// not full path, get previous level directory string
|
||||
var indexOfSeparator = path.LastIndexOf('\\');
|
||||
|
||||
return path.Substring(0, indexOfSeparator + 1);
|
||||
return path[..(indexOfSeparator + 1)];
|
||||
}
|
||||
|
||||
return path;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
using Microsoft.Win32;
|
||||
using System;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace Flow.Launcher.Plugin.SharedCommands
|
||||
{
|
||||
|
|
@ -13,7 +14,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
{
|
||||
private static string GetDefaultBrowserPath()
|
||||
{
|
||||
string name = string.Empty;
|
||||
var name = string.Empty;
|
||||
try
|
||||
{
|
||||
using var regDefault = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", false);
|
||||
|
|
@ -23,8 +24,7 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
name = regKey.GetValue(null).ToString().ToLower().Replace("\"", "");
|
||||
|
||||
if (!name.EndsWith("exe"))
|
||||
name = name.Substring(0, name.LastIndexOf(".exe") + 4);
|
||||
|
||||
name = name[..(name.LastIndexOf(".exe") + 4)];
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
|
@ -65,12 +65,21 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
{
|
||||
Process.Start(psi)?.Dispose();
|
||||
}
|
||||
catch (System.ComponentModel.Win32Exception)
|
||||
// This error may be thrown if browser path is incorrect
|
||||
catch (Win32Exception)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
try
|
||||
{
|
||||
FileName = url, UseShellExecute = true
|
||||
});
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = url,
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw; // Re-throw the exception if we cannot open the URL in the default browser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,12 +109,20 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
Process.Start(psi)?.Dispose();
|
||||
}
|
||||
// This error may be thrown if browser path is incorrect
|
||||
catch (System.ComponentModel.Win32Exception)
|
||||
catch (Win32Exception)
|
||||
{
|
||||
Process.Start(new ProcessStartInfo
|
||||
try
|
||||
{
|
||||
FileName = url, UseShellExecute = true
|
||||
});
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = url,
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw; // Re-throw the exception if we cannot open the URL in the default browser
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
77
Flow.Launcher.Plugin/SharedModels/ThemeData.cs
Normal file
77
Flow.Launcher.Plugin/SharedModels/ThemeData.cs
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
|
||||
namespace Flow.Launcher.Plugin.SharedModels;
|
||||
|
||||
/// <summary>
|
||||
/// Theme data model
|
||||
/// </summary>
|
||||
public class ThemeData
|
||||
{
|
||||
/// <summary>
|
||||
/// Theme file name without extension
|
||||
/// </summary>
|
||||
public string FileNameWithoutExtension { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Theme name
|
||||
/// </summary>
|
||||
public string Name { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the theme supports dark mode
|
||||
/// </summary>
|
||||
public bool? IsDark { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the theme supports blur effects
|
||||
/// </summary>
|
||||
public bool? HasBlur { get; private init; }
|
||||
|
||||
/// <summary>
|
||||
/// Theme data constructor
|
||||
/// </summary>
|
||||
public ThemeData(string fileNameWithoutExtension, string name, bool? isDark = null, bool? hasBlur = null)
|
||||
{
|
||||
FileNameWithoutExtension = fileNameWithoutExtension;
|
||||
Name = name;
|
||||
IsDark = isDark;
|
||||
HasBlur = hasBlur;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator ==(ThemeData left, ThemeData right)
|
||||
{
|
||||
if (left is null && right is null)
|
||||
return true;
|
||||
if (left is null || right is null)
|
||||
return false;
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public static bool operator !=(ThemeData left, ThemeData right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is not ThemeData other)
|
||||
return false;
|
||||
return FileNameWithoutExtension == other.FileNameWithoutExtension &&
|
||||
Name == other.Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(FileNameWithoutExtension, Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
|
@ -39,8 +39,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("C:\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY System.FileName")]
|
||||
[TestCase("C:\\SomeFolder\\", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY System.FileName")]
|
||||
[TestCase("C:\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("C:\\SomeFolder\\", $"SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType FROM SystemIndex WHERE directory='file:C:\\SomeFolder\\' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
|
||||
{
|
||||
// Given
|
||||
|
|
@ -59,7 +59,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestCase("C:\\SomeFolder", "flow.launcher.sln", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType" +
|
||||
" FROM SystemIndex WHERE directory='file:C:\\SomeFolder'" +
|
||||
" AND (System.FileName LIKE 'flow.launcher.sln%' OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"'))" +
|
||||
" ORDER BY System.FileName")]
|
||||
$" ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
|
||||
string folderPath, string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
@ -87,8 +87,8 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("flow.launcher.sln", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" " +
|
||||
"FROM \"SystemIndex\" WHERE (System.FileName LIKE 'flow.launcher.sln%' " +
|
||||
"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY System.FileName")]
|
||||
[TestCase("", "SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY System.FileName")]
|
||||
$"OR CONTAINS(System.FileName,'\"flow.launcher.sln*\"',1033)) AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
[TestCase("", $"SELECT TOP 100 \"System.FileName\", \"System.ItemUrl\", \"System.ItemType\" FROM \"SystemIndex\" WHERE WorkId IS NOT NULL AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
@ -107,7 +107,6 @@ namespace Flow.Launcher.Test.Plugins
|
|||
ClassicAssert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase(@"some words", @"FREETEXT('some words')")]
|
||||
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
|
||||
|
|
@ -127,7 +126,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("some words", "SELECT TOP 100 System.FileName, System.ItemUrl, System.ItemType " +
|
||||
"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY System.FileName")]
|
||||
$"FROM SystemIndex WHERE FREETEXT('some words') AND scope='file:' ORDER BY {QueryConstructor.OrderIdentifier}")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@
|
|||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="26,12,26,0">
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,12">
|
||||
<StackPanel Margin="26 12 26 0">
|
||||
<StackPanel Grid.Row="0" Margin="0 0 0 12">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
Margin="0 0 0 0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource actionKeywordsTitle}"
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
TextWrapping="WrapWithOverflow" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,18,0,0" Orientation="Horizontal">
|
||||
<StackPanel Margin="0 18 0 0" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
|
|
@ -83,14 +83,14 @@
|
|||
x:Name="tbOldActionKeyword"
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="14,10,10,10"
|
||||
Margin="14 10 10 10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource Color05B}" />
|
||||
</StackPanel>
|
||||
<StackPanel Margin="0,0,0,10" Orientation="Horizontal">
|
||||
<StackPanel Margin="0 0 0 10" Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
<TextBox
|
||||
x:Name="tbAction"
|
||||
Width="105"
|
||||
Margin="10,10,15,10"
|
||||
Margin="10 10 15 10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
|
|
@ -112,20 +112,20 @@
|
|||
Grid.Row="1"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0">
|
||||
BorderThickness="0 1 0 0">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
Width="145"
|
||||
Height="30"
|
||||
Margin="10,0,5,0"
|
||||
Margin="10 0 5 0"
|
||||
Click="BtnCancel_OnClick"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button
|
||||
x:Name="btnDone"
|
||||
Width="145"
|
||||
Height="30"
|
||||
Margin="5,0,10,0"
|
||||
Margin="5 0 10 0"
|
||||
Click="btnDone_OnClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using System.Windows;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Flow.Launcher.Core;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
|
@ -10,20 +8,19 @@ namespace Flow.Launcher
|
|||
{
|
||||
public partial class ActionKeywords
|
||||
{
|
||||
private readonly PluginPair plugin;
|
||||
private readonly Internationalization translater = InternationalizationManager.Instance;
|
||||
private readonly PluginViewModel pluginViewModel;
|
||||
private readonly PluginPair _plugin;
|
||||
private readonly PluginViewModel _pluginViewModel;
|
||||
|
||||
public ActionKeywords(PluginViewModel pluginViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
plugin = pluginViewModel.PluginPair;
|
||||
this.pluginViewModel = pluginViewModel;
|
||||
_plugin = pluginViewModel.PluginPair;
|
||||
_pluginViewModel = pluginViewModel;
|
||||
}
|
||||
|
||||
private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeparator, plugin.Metadata.ActionKeywords.ToArray());
|
||||
tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeparator, _plugin.Metadata.ActionKeywords.ToArray());
|
||||
tbAction.Focus();
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +31,7 @@ namespace Flow.Launcher
|
|||
|
||||
private void btnDone_OnClick(object sender, RoutedEventArgs _)
|
||||
{
|
||||
var oldActionKeywords = plugin.Metadata.ActionKeywords;
|
||||
var oldActionKeywords = _plugin.Metadata.ActionKeywords;
|
||||
|
||||
var newActionKeywords = tbAction.Text.Split(Query.ActionKeywordSeparator).ToList();
|
||||
newActionKeywords.RemoveAll(string.IsNullOrEmpty);
|
||||
|
|
@ -48,7 +45,7 @@ namespace Flow.Launcher
|
|||
{
|
||||
if (oldActionKeywords.Count != newActionKeywords.Count)
|
||||
{
|
||||
ReplaceActionKeyword(plugin.Metadata.ID, removedActionKeywords, addedActionKeywords);
|
||||
ReplaceActionKeyword(_plugin.Metadata.ID, removedActionKeywords, addedActionKeywords);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -58,18 +55,16 @@ namespace Flow.Launcher
|
|||
if (sortedOldActionKeywords.SequenceEqual(sortedNewActionKeywords))
|
||||
{
|
||||
// User just changes the sequence of action keywords
|
||||
var msg = translater.GetTranslation("newActionKeywordsSameAsOld");
|
||||
MessageBoxEx.Show(msg);
|
||||
App.API.ShowMsgBox(App.API.GetTranslation("newActionKeywordsSameAsOld"));
|
||||
}
|
||||
else
|
||||
{
|
||||
ReplaceActionKeyword(plugin.Metadata.ID, removedActionKeywords, addedActionKeywords);
|
||||
ReplaceActionKeyword(_plugin.Metadata.ID, removedActionKeywords, addedActionKeywords);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string msg = translater.GetTranslation("newActionKeywordsHasBeenAssigned");
|
||||
App.API.ShowMsgBox(msg);
|
||||
App.API.ShowMsgBox(App.API.GetTranslation("newActionKeywordsHasBeenAssigned"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +80,7 @@ namespace Flow.Launcher
|
|||
}
|
||||
|
||||
// Update action keywords text and close window
|
||||
pluginViewModel.OnActionKeywordsChanged();
|
||||
_pluginViewModel.OnActionKeywordsTextChanged();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Text;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
|
|
@ -18,10 +19,11 @@ using Flow.Launcher.Infrastructure.Logger;
|
|||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.SettingPages.ViewModels;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
using Microsoft.VisualStudio.Threading;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
|
|
@ -30,13 +32,16 @@ namespace Flow.Launcher
|
|||
#region Public Properties
|
||||
|
||||
public static IPublicAPI API { get; private set; }
|
||||
public static bool LoadingOrExiting => _mainWindow == null || _mainWindow.CanClose;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static readonly string ClassName = nameof(App);
|
||||
|
||||
private static bool _disposed;
|
||||
private MainWindow _mainWindow;
|
||||
private static MainWindow _mainWindow;
|
||||
private readonly MainViewModel _mainVM;
|
||||
private readonly Settings _settings;
|
||||
|
||||
|
|
@ -76,14 +81,31 @@ namespace Flow.Launcher
|
|||
Launcher.Properties.Settings.Default.GithubRepo,
|
||||
Launcher.Properties.Settings.Default.PrereleaseRepo))
|
||||
.AddSingleton<Portable>()
|
||||
.AddSingleton<SettingWindowViewModel>()
|
||||
.AddSingleton<IAlphabet, PinyinAlphabet>()
|
||||
.AddSingleton<StringMatcher>()
|
||||
.AddSingleton<Internationalization>()
|
||||
.AddSingleton<IPublicAPI, PublicAPIInstance>()
|
||||
.AddSingleton<MainViewModel>()
|
||||
.AddSingleton<Theme>()
|
||||
// Use one instance for main window view model because we only have one main window
|
||||
.AddSingleton<MainViewModel>()
|
||||
// Use one instance for welcome window view model & setting window view model because
|
||||
// pages in welcome window & setting window need to share the same instance and
|
||||
// these two view models do not need to be reset when creating new windows
|
||||
.AddSingleton<WelcomeViewModel>()
|
||||
.AddSingleton<SettingWindowViewModel>()
|
||||
// Use transient instance for setting window page view models because
|
||||
// pages in setting window need to be recreated when setting window is closed
|
||||
.AddTransient<SettingsPaneAboutViewModel>()
|
||||
.AddTransient<SettingsPaneGeneralViewModel>()
|
||||
.AddTransient<SettingsPaneHotkeyViewModel>()
|
||||
.AddTransient<SettingsPanePluginsViewModel>()
|
||||
.AddTransient<SettingsPanePluginStoreViewModel>()
|
||||
.AddTransient<SettingsPaneProxyViewModel>()
|
||||
.AddTransient<SettingsPaneThemeViewModel>()
|
||||
// Use transient instance for dialog view models because
|
||||
// settings will change and we need to recreate them
|
||||
.AddTransient<SelectBrowserViewModel>()
|
||||
.AddTransient<SelectFileManagerViewModel>()
|
||||
).Build();
|
||||
Ioc.Default.ConfigureServices(host.Services);
|
||||
}
|
||||
|
|
@ -140,7 +162,7 @@ namespace Flow.Launcher
|
|||
|
||||
private async void OnStartup(object sender, StartupEventArgs e)
|
||||
{
|
||||
await Stopwatch.NormalAsync("|App.OnStartup|Startup cost", async () =>
|
||||
await API.StopwatchLogInfoAsync(ClassName, "Startup cost", async () =>
|
||||
{
|
||||
// Because new message box api uses MessageBoxEx window,
|
||||
// if it is created and closed before main window is created, it will cause the application to exit.
|
||||
|
|
@ -149,13 +171,20 @@ namespace Flow.Launcher
|
|||
|
||||
Log.SetLogLevel(_settings.LogLevel);
|
||||
|
||||
// Update dynamic resources base on settings
|
||||
Current.Resources["SettingWindowFont"] = new FontFamily(_settings.SettingWindowFont);
|
||||
Current.Resources["ContentControlThemeFontFamily"] = new FontFamily(_settings.SettingWindowFont);
|
||||
|
||||
Notification.Install();
|
||||
|
||||
Ioc.Default.GetRequiredService<Portable>().PreStartCleanUpAfterPortabilityUpdate();
|
||||
|
||||
Log.Info("|App.OnStartup|Begin Flow Launcher startup ----------------------------------------------------");
|
||||
Log.Info($"|App.OnStartup|Runtime info:{ErrorReporting.RuntimeInfo()}");
|
||||
API.LogInfo(ClassName, "Begin Flow Launcher startup ----------------------------------------------------");
|
||||
API.LogInfo(ClassName, $"Runtime info:{ErrorReporting.RuntimeInfo()}");
|
||||
|
||||
RegisterAppDomainExceptions();
|
||||
RegisterDispatcherUnhandledException();
|
||||
RegisterTaskSchedulerUnhandledException();
|
||||
|
||||
var imageLoadertask = ImageLoader.InitializeAsync();
|
||||
|
||||
|
|
@ -171,19 +200,20 @@ namespace Flow.Launcher
|
|||
await PluginManager.InitializePluginsAsync();
|
||||
|
||||
// Change language after all plugins are initialized because we need to update plugin title based on their api
|
||||
// TODO: Clean InternationalizationManager.Instance and InternationalizationManager.Instance.GetTranslation in future
|
||||
await Ioc.Default.GetRequiredService<Internationalization>().InitializeLanguageAsync();
|
||||
|
||||
await imageLoadertask;
|
||||
|
||||
_mainWindow = new MainWindow();
|
||||
|
||||
Log.Info($"|App.OnStartup|Dependencies Info:{ErrorReporting.DependenciesInfo()}");
|
||||
|
||||
Current.MainWindow = _mainWindow;
|
||||
Current.MainWindow.Title = Constant.FlowLauncher;
|
||||
|
||||
// main windows needs initialized before theme change because of blur settings
|
||||
// Initialize hotkey mapper instantly after main window is created because
|
||||
// it will steal focus from main window which causes window hide
|
||||
HotKeyMapper.Initialize();
|
||||
|
||||
// Initialize theme for main window
|
||||
Ioc.Default.GetRequiredService<Theme>().ChangeTheme();
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
|
|
@ -194,28 +224,25 @@ namespace Flow.Launcher
|
|||
AutoUpdates();
|
||||
|
||||
API.SaveAppAllSettings();
|
||||
Log.Info("|App.OnStartup|End Flow Launcher startup ----------------------------------------------------");
|
||||
API.LogInfo(ClassName, "End Flow Launcher startup ----------------------------------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
#pragma warning restore VSTHRD100 // Avoid async void methods
|
||||
|
||||
/// <summary>
|
||||
/// Check startup only for Release
|
||||
/// </summary>
|
||||
[Conditional("RELEASE")]
|
||||
private void AutoStartup()
|
||||
{
|
||||
// we try to enable auto-startup on first launch, or reenable if it was removed
|
||||
// but the user still has the setting set
|
||||
if (_settings.StartFlowLauncherOnSystemStartup && !Helper.AutoStartup.IsEnabled)
|
||||
if (_settings.StartFlowLauncherOnSystemStartup)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_settings.UseLogonTaskForStartup)
|
||||
{
|
||||
Helper.AutoStartup.EnableViaLogonTask();
|
||||
}
|
||||
else
|
||||
{
|
||||
Helper.AutoStartup.EnableViaRegistry();
|
||||
}
|
||||
Helper.AutoStartup.CheckIsEnabled(_settings.UseLogonTaskForStartup);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -227,6 +254,7 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
[Conditional("RELEASE")]
|
||||
private void AutoUpdates()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
|
|
@ -252,25 +280,25 @@ namespace Flow.Launcher
|
|||
{
|
||||
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
|
||||
{
|
||||
Log.Info("|App.RegisterExitEvents|Process Exit");
|
||||
API.LogInfo(ClassName, "Process Exit");
|
||||
Dispose();
|
||||
};
|
||||
|
||||
Current.Exit += (s, e) =>
|
||||
{
|
||||
Log.Info("|App.RegisterExitEvents|Application Exit");
|
||||
API.LogInfo(ClassName, "Application Exit");
|
||||
Dispose();
|
||||
};
|
||||
|
||||
Current.SessionEnding += (s, e) =>
|
||||
{
|
||||
Log.Info("|App.RegisterExitEvents|Session Ending");
|
||||
API.LogInfo(ClassName, "Session Ending");
|
||||
Dispose();
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// let exception throw as normal is better for Debug
|
||||
/// Let exception throw as normal is better for Debug
|
||||
/// </summary>
|
||||
[Conditional("RELEASE")]
|
||||
private void RegisterDispatcherUnhandledException()
|
||||
|
|
@ -279,12 +307,20 @@ namespace Flow.Launcher
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// let exception throw as normal is better for Debug
|
||||
/// Let exception throw as normal is better for Debug
|
||||
/// </summary>
|
||||
[Conditional("RELEASE")]
|
||||
private static void RegisterAppDomainExceptions()
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledExceptionHandle;
|
||||
AppDomain.CurrentDomain.UnhandledException += ErrorReporting.UnhandledException;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Let exception throw as normal is better for Debug
|
||||
/// </summary>
|
||||
private static void RegisterTaskSchedulerUnhandledException()
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException += ErrorReporting.TaskSchedulerUnobservedTaskException;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
@ -317,9 +353,9 @@ namespace Flow.Launcher
|
|||
_disposed = true;
|
||||
}
|
||||
|
||||
Stopwatch.Normal("|App.Dispose|Dispose cost", () =>
|
||||
API.StopwatchLogInfo(ClassName, "Dispose cost", () =>
|
||||
{
|
||||
Log.Info("|App.Dispose|Begin Flow Launcher dispose ----------------------------------------------------");
|
||||
API.LogInfo(ClassName, "Begin Flow Launcher dispose ----------------------------------------------------");
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
|
|
@ -329,7 +365,7 @@ namespace Flow.Launcher
|
|||
_mainVM?.Dispose();
|
||||
}
|
||||
|
||||
Log.Info("|App.Dispose|End Flow Launcher dispose ----------------------------------------------------");
|
||||
API.LogInfo(ClassName, "End Flow Launcher dispose ----------------------------------------------------");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -346,7 +382,7 @@ namespace Flow.Launcher
|
|||
|
||||
public void OnSecondAppStarted()
|
||||
{
|
||||
Ioc.Default.GetRequiredService<MainViewModel>().Show();
|
||||
API.ShowMainWindow();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
|||
32
Flow.Launcher/Converters/BadgePositionConverter.cs
Normal file
32
Flow.Launcher/Converters/BadgePositionConverter.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Flow.Launcher.Converters;
|
||||
|
||||
public class BadgePositionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double actualWidth && parameter is string param)
|
||||
{
|
||||
double offset = actualWidth / 2 - 8;
|
||||
|
||||
if (param == "1") // X-Offset
|
||||
{
|
||||
return offset + 2;
|
||||
}
|
||||
else if (param == "2") // Y-Offset
|
||||
{
|
||||
return offset + 2;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
namespace Flow.Launcher.Converters;
|
||||
|
||||
public class QuerySuggestionBoxConverter : IMultiValueConverter
|
||||
{
|
||||
private static readonly string ClassName = nameof(QuerySuggestionBoxConverter);
|
||||
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
// values[0] is TextBox: The textbox displaying the autocomplete suggestion
|
||||
|
|
@ -43,8 +45,16 @@ public class QuerySuggestionBoxConverter : IMultiValueConverter
|
|||
|
||||
// Check if Text will be larger than our QueryTextBox
|
||||
Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch);
|
||||
// TODO: Obsolete warning?
|
||||
var ft = new FormattedText(queryTextBox.Text, CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black);
|
||||
var dpi = VisualTreeHelper.GetDpi(queryTextBox);
|
||||
var ft = new FormattedText(
|
||||
queryTextBox.Text,
|
||||
CultureInfo.CurrentCulture,
|
||||
FlowDirection.LeftToRight,
|
||||
typeface,
|
||||
queryTextBox.FontSize,
|
||||
Brushes.Black,
|
||||
dpi.PixelsPerDip
|
||||
);
|
||||
|
||||
var offset = queryTextBox.Padding.Right;
|
||||
|
||||
|
|
@ -55,7 +65,7 @@ public class QuerySuggestionBoxConverter : IMultiValueConverter
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e);
|
||||
App.API.LogException(ClassName, "fail to convert text for suggestion box", e);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
27
Flow.Launcher/Converters/SizeRatioConverter.cs
Normal file
27
Flow.Launcher/Converters/SizeRatioConverter.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System.Windows.Data;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
|
||||
namespace Flow.Launcher.Converters;
|
||||
|
||||
public class SizeRatioConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double size && parameter is string ratioString)
|
||||
{
|
||||
if (double.TryParse(ratioString, NumberStyles.Any, CultureInfo.InvariantCulture, out double ratio))
|
||||
{
|
||||
return size * ratio;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
namespace Flow.Launcher.Converters;
|
||||
|
|
@ -23,7 +22,7 @@ public class TextConverter : IValueConverter
|
|||
if (translationKey is null)
|
||||
return id;
|
||||
|
||||
return InternationalizationManager.Instance.GetTranslation(translationKey);
|
||||
return App.API.GetTranslation(translationKey);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException();
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Controls;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class CustomQueryHotkeySetting : Window
|
||||
{
|
||||
private readonly Settings _settings;
|
||||
|
||||
private bool update;
|
||||
private CustomPluginHotkey updateCustomHotkey;
|
||||
|
||||
|
|
@ -53,14 +53,13 @@ namespace Flow.Launcher
|
|||
Close();
|
||||
}
|
||||
|
||||
|
||||
public void UpdateItem(CustomPluginHotkey item)
|
||||
{
|
||||
updateCustomHotkey = _settings.CustomPluginHotkeys.FirstOrDefault(o =>
|
||||
o.ActionKeyword == item.ActionKeyword && o.Hotkey == item.Hotkey);
|
||||
if (updateCustomHotkey == null)
|
||||
{
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("invalidPluginHotkey"));
|
||||
App.API.ShowMsgBox(App.API.GetTranslation("invalidPluginHotkey"));
|
||||
Close();
|
||||
return;
|
||||
}
|
||||
|
|
@ -68,7 +67,7 @@ namespace Flow.Launcher
|
|||
tbAction.Text = updateCustomHotkey.ActionKeyword;
|
||||
HotkeyControl.SetHotkey(updateCustomHotkey.Hotkey, false);
|
||||
update = true;
|
||||
lblAdd.Text = InternationalizationManager.Instance.GetTranslation("update");
|
||||
lblAdd.Text = App.API.GetTranslation("update");
|
||||
}
|
||||
|
||||
private void BtnTestActionKeyword_OnClick(object sender, RoutedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -56,11 +56,11 @@
|
|||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="26,0,26,0">
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,12">
|
||||
<StackPanel Margin="26 0 26 0">
|
||||
<StackPanel Grid.Row="0" Margin="0 0 0 12">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
Margin="0 0 0 0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource customQueryShortcut}"
|
||||
|
|
@ -73,18 +73,18 @@
|
|||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<TextBlock
|
||||
Margin="0,20,0,0"
|
||||
Margin="0 20 0 0"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customeQueryShortcutGuide}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<Image
|
||||
Width="478"
|
||||
Margin="0,20,0,0"
|
||||
Margin="0 20 0 0"
|
||||
Source="/Images/illustration_02.png" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,10,0,10" Orientation="Horizontal">
|
||||
<StackPanel Margin="0 10 0 10" Orientation="Horizontal">
|
||||
<Grid Width="478">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
|
|
@ -124,14 +124,14 @@
|
|||
LastChildFill="True">
|
||||
<Button
|
||||
x:Name="btnTestShortcut"
|
||||
Margin="0,0,10,0"
|
||||
Padding="10,5,10,5"
|
||||
Margin="0 0 10 0"
|
||||
Padding="10 5 10 5"
|
||||
Click="BtnTestShortcut_OnClick"
|
||||
Content="{DynamicResource preview}"
|
||||
DockPanel.Dock="Right" />
|
||||
<TextBox
|
||||
x:Name="tbExpand"
|
||||
Margin="10,0,10,0"
|
||||
Margin="10 0 10 0"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Value}" />
|
||||
|
|
@ -142,21 +142,21 @@
|
|||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
Margin="0 10 0 0"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0">
|
||||
BorderThickness="0 1 0 0">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
MinWidth="140"
|
||||
Margin="10,0,5,0"
|
||||
Margin="10 0 5 0"
|
||||
Click="BtnCancel_OnClick"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button
|
||||
x:Name="btnAdd"
|
||||
MinWidth="140"
|
||||
Margin="5,0,10,0"
|
||||
Margin="5 0 10 0"
|
||||
Click="BtnAdd_OnClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using Flow.Launcher.SettingPages.ViewModels;
|
||||
using Flow.Launcher.Core;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class CustomShortcutSetting : Window
|
||||
{
|
||||
private readonly SettingsPaneHotkeyViewModel _hotkeyVm;
|
||||
public string Key { get; set; } = String.Empty;
|
||||
public string Value { get; set; } = String.Empty;
|
||||
public string Key { get; set; } = string.Empty;
|
||||
public string Value { get; set; } = string.Empty;
|
||||
private string originalKey { get; } = null;
|
||||
private string originalValue { get; } = null;
|
||||
private bool update { get; } = false;
|
||||
|
|
@ -41,15 +38,15 @@ namespace Flow.Launcher
|
|||
|
||||
private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value))
|
||||
if (string.IsNullOrEmpty(Key) || string.IsNullOrEmpty(Value))
|
||||
{
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
|
||||
App.API.ShowMsgBox(App.API.GetTranslation("emptyShortcut"));
|
||||
return;
|
||||
}
|
||||
// Check if key is modified or adding a new one
|
||||
if (((update && originalKey != Key) || !update) && _hotkeyVm.DoesShortcutExist(Key))
|
||||
{
|
||||
App.API.ShowMsgBox(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
|
||||
App.API.ShowMsgBox(App.API.GetTranslation("duplicateShortcut"));
|
||||
return;
|
||||
}
|
||||
DialogResult = !update || originalKey != Key || originalValue != Value;
|
||||
|
|
|
|||
|
|
@ -90,6 +90,11 @@
|
|||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
||||
<PackageReference Include="MdXaml" Version="1.27.0" />
|
||||
<PackageReference Include="MdXaml.AnimatedGif" Version="1.27.0" />
|
||||
<PackageReference Include="MdXaml.Html" Version="1.27.0" />
|
||||
<PackageReference Include="MdXaml.Plugins" Version="1.27.0" />
|
||||
<PackageReference Include="MdXaml.Svg" Version="1.27.0" />
|
||||
<!-- Do not upgrade Microsoft.Extensions.DependencyInjection and Microsoft.Extensions.Hosting since we are .Net7.0 -->
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
|
|
@ -98,7 +103,9 @@
|
|||
<!-- https://github.com/Flow-Launcher/Flow.Launcher/issues/1772#issuecomment-1502440801 -->
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
|
||||
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
|
||||
<PackageReference Include="TaskScheduler" Version="2.12.1" />
|
||||
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
|
||||
|
|
|
|||
|
|
@ -1,43 +1,53 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.TaskScheduler;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
public class AutoStartup
|
||||
{
|
||||
private static readonly string ClassName = nameof(AutoStartup);
|
||||
|
||||
private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
|
||||
private const string LogonTaskName = $"{Constant.FlowLauncher} Startup";
|
||||
private const string LogonTaskDesc = $"{Constant.FlowLauncher} Auto Startup";
|
||||
|
||||
public static bool IsEnabled
|
||||
public static void CheckIsEnabled(bool useLogonTaskForStartup)
|
||||
{
|
||||
get
|
||||
// We need to check both because if both of them are enabled,
|
||||
// Hide Flow Launcher on startup will not work since the later one will trigger main window show event
|
||||
var logonTaskEnabled = CheckLogonTask();
|
||||
var registryEnabled = CheckRegistry();
|
||||
if (useLogonTaskForStartup)
|
||||
{
|
||||
// Check if logon task is enabled
|
||||
if (CheckLogonTask())
|
||||
// Enable logon task
|
||||
if (!logonTaskEnabled)
|
||||
{
|
||||
return true;
|
||||
Enable(true);
|
||||
}
|
||||
|
||||
// Check if registry is enabled
|
||||
try
|
||||
// Disable registry
|
||||
if (registryEnabled)
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
var path = key?.GetValue(Constant.FlowLauncher) as string;
|
||||
return path == Constant.ExecutablePath;
|
||||
Disable(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enable registry
|
||||
if (!registryEnabled)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}");
|
||||
Enable(false);
|
||||
}
|
||||
// Disable logon task
|
||||
if (logonTaskEnabled)
|
||||
{
|
||||
Disable(true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -50,40 +60,63 @@ public class AutoStartup
|
|||
try
|
||||
{
|
||||
// Check if the action is the same as the current executable path
|
||||
var action = task.Definition.Actions.FirstOrDefault()!.ToString().Trim();
|
||||
if (!Constant.ExecutablePath.Equals(action, StringComparison.OrdinalIgnoreCase) && !File.Exists(action))
|
||||
// If not, we need to unschedule and reschedule the task
|
||||
if (task.Definition.Actions.FirstOrDefault() is Microsoft.Win32.TaskScheduler.Action taskAction)
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
ScheduleLogonTask();
|
||||
var action = taskAction.ToString().Trim();
|
||||
if (!action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
UnscheduleLogonTask();
|
||||
ScheduleLogonTask();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to check logon task: {e}");
|
||||
App.API.LogError(ClassName, $"Failed to check logon task: {e}");
|
||||
throw; // Throw exception so that App.AutoStartup can show error message
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool CheckRegistry()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
if (key != null)
|
||||
{
|
||||
// Check if the action is the same as the current executable path
|
||||
// If not, we need to unschedule and reschedule the task
|
||||
var action = (key.GetValue(Constant.FlowLauncher) as string) ?? string.Empty;
|
||||
if (!action.Equals(Constant.ExecutablePath, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
UnscheduleRegistry();
|
||||
ScheduleRegistry();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
App.API.LogError(ClassName, $"Failed to check registry: {e}");
|
||||
throw; // Throw exception so that App.AutoStartup can show error message
|
||||
}
|
||||
}
|
||||
|
||||
public static void DisableViaLogonTaskAndRegistry()
|
||||
{
|
||||
Disable(true);
|
||||
Disable(false);
|
||||
}
|
||||
|
||||
public static void EnableViaLogonTask()
|
||||
{
|
||||
Enable(true);
|
||||
}
|
||||
|
||||
public static void EnableViaRegistry()
|
||||
{
|
||||
Enable(false);
|
||||
}
|
||||
|
||||
public static void ChangeToViaLogonTask()
|
||||
{
|
||||
Disable(false);
|
||||
|
|
@ -106,13 +139,12 @@ public class AutoStartup
|
|||
}
|
||||
else
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.DeleteValue(Constant.FlowLauncher, false);
|
||||
UnscheduleRegistry();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}");
|
||||
App.API.LogError(ClassName, $"Failed to disable auto-startup: {e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -127,13 +159,12 @@ public class AutoStartup
|
|||
}
|
||||
else
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
|
||||
ScheduleRegistry();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}");
|
||||
App.API.LogError(ClassName, $"Failed to enable auto-startup: {e}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +192,7 @@ public class AutoStartup
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to schedule logon task: {e}");
|
||||
App.API.LogError(ClassName, $"Failed to schedule logon task: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +207,7 @@ public class AutoStartup
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error("AutoStartup", $"Failed to unschedule logon task: {e}");
|
||||
App.API.LogError(ClassName, $"Failed to unschedule logon task: {e}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -187,4 +218,18 @@ public class AutoStartup
|
|||
var principal = new WindowsPrincipal(identity);
|
||||
return principal.IsInRole(WindowsBuiltInRole.Administrator);
|
||||
}
|
||||
|
||||
private static bool UnscheduleRegistry()
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.DeleteValue(Constant.FlowLauncher, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool ScheduleRegistry()
|
||||
{
|
||||
using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true);
|
||||
key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\"");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,18 +28,18 @@ public class DataWebRequestFactory : IWebRequestCreate
|
|||
|
||||
public DataWebResponse(Uri uri)
|
||||
{
|
||||
string uriString = uri.AbsoluteUri;
|
||||
var uriString = uri.AbsoluteUri;
|
||||
|
||||
int commaIndex = uriString.IndexOf(',');
|
||||
var headers = uriString.Substring(0, commaIndex).Split(';');
|
||||
var commaIndex = uriString.IndexOf(',');
|
||||
var headers = uriString[..commaIndex].Split(';');
|
||||
_contentType = headers[0];
|
||||
string dataString = uriString.Substring(commaIndex + 1);
|
||||
var dataString = uriString[(commaIndex + 1)..];
|
||||
_data = Convert.FromBase64String(dataString);
|
||||
}
|
||||
|
||||
public override string ContentType
|
||||
{
|
||||
get { return _contentType; }
|
||||
get => _contentType;
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
|
@ -48,7 +48,7 @@ public class DataWebRequestFactory : IWebRequestCreate
|
|||
|
||||
public override long ContentLength
|
||||
{
|
||||
get { return _data.Length; }
|
||||
get => _data.Length;
|
||||
set
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
|
|
|
|||
|
|
@ -1,35 +1,48 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Threading;
|
||||
using NLog;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Infrastructure.Exception;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using NLog;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
public static class ErrorReporting
|
||||
{
|
||||
private static void Report(Exception e)
|
||||
private static void Report(Exception e, bool silent = false, [CallerMemberName] string methodName = "UnHandledException")
|
||||
{
|
||||
var logger = LogManager.GetLogger("UnHandledException");
|
||||
var logger = LogManager.GetLogger(methodName);
|
||||
logger.Fatal(ExceptionFormatter.FormatExcpetion(e));
|
||||
if (silent) return;
|
||||
var reportWindow = new ReportWindow(e);
|
||||
reportWindow.Show();
|
||||
}
|
||||
|
||||
public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e)
|
||||
public static void UnhandledException(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
//handle non-ui thread exceptions
|
||||
// handle non-ui thread exceptions
|
||||
Report((Exception)e.ExceptionObject);
|
||||
}
|
||||
|
||||
public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
|
||||
{
|
||||
//handle ui thread exceptions
|
||||
// handle ui thread exceptions
|
||||
Report(e.Exception);
|
||||
//prevent application exist, so the user can copy prompted error info
|
||||
// prevent application exist, so the user can copy prompted error info
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
public static void TaskSchedulerUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
|
||||
{
|
||||
// log exception but do not handle unobserved task exceptions on UI thread
|
||||
//Application.Current.Dispatcher.Invoke(() => Report(e.Exception, true));
|
||||
Log.Exception(nameof(ErrorReporting), "Unobserved task exception occurred.", e.Exception);
|
||||
// prevent application exit, so the user can copy the prompted error info
|
||||
e.SetObserved();
|
||||
}
|
||||
|
||||
public static string RuntimeInfo()
|
||||
{
|
||||
var info =
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@ using Flow.Launcher.Infrastructure.UserSettings;
|
|||
using System;
|
||||
using NHotkey;
|
||||
using NHotkey.Wpf;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using ChefKeys;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
|
||||
namespace Flow.Launcher.Helper;
|
||||
|
||||
internal static class HotKeyMapper
|
||||
{
|
||||
private static readonly string ClassName = nameof(HotKeyMapper);
|
||||
|
||||
private static Settings _settings;
|
||||
private static MainViewModel _mainViewModel;
|
||||
|
||||
|
|
@ -52,13 +52,13 @@ internal static class HotKeyMapper
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(
|
||||
App.API.LogError(ClassName,
|
||||
string.Format("|HotkeyMapper.SetWithChefKeys|Error registering hotkey: {0} \nStackTrace:{1}",
|
||||
e.Message,
|
||||
e.StackTrace));
|
||||
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
|
||||
MessageBoxEx.Show(errorMsg, errorMsgTitle);
|
||||
string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,13 +77,13 @@ internal static class HotKeyMapper
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(
|
||||
App.API.LogError(ClassName,
|
||||
string.Format("|HotkeyMapper.SetHotkey|Error registering hotkey {2}: {0} \nStackTrace:{1}",
|
||||
e.Message,
|
||||
e.StackTrace,
|
||||
hotkeyStr));
|
||||
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
|
||||
string errorMsg = string.Format(App.API.GetTranslation("registerHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
|
@ -103,13 +103,13 @@ internal static class HotKeyMapper
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error(
|
||||
App.API.LogError(ClassName,
|
||||
string.Format("|HotkeyMapper.RemoveHotkey|Error removing hotkey: {0} \nStackTrace:{1}",
|
||||
e.Message,
|
||||
e.StackTrace));
|
||||
string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("unregisterHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle");
|
||||
MessageBoxEx.Show(errorMsg, errorMsgTitle);
|
||||
string errorMsg = string.Format(App.API.GetTranslation("unregisterHotkeyFailed"), hotkeyStr);
|
||||
string errorMsgTitle = App.API.GetTranslation("MessageBoxTitle");
|
||||
App.API.ShowMsgBox(errorMsg, errorMsgTitle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,8 +137,8 @@ internal static class HotKeyMapper
|
|||
if (_mainViewModel.ShouldIgnoreHotkeys())
|
||||
return;
|
||||
|
||||
_mainViewModel.Show();
|
||||
_mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true);
|
||||
App.API.ShowMainWindow();
|
||||
App.API.ChangeQuery(hotkey.ActionKeyword, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ namespace Flow.Launcher.Helper;
|
|||
|
||||
public static class WallpaperPathRetrieval
|
||||
{
|
||||
private static readonly string ClassName = nameof(WallpaperPathRetrieval);
|
||||
|
||||
private const int MaxCacheSize = 3;
|
||||
private static readonly Dictionary<(string, DateTime), ImageBrush> WallpaperCache = new();
|
||||
private static readonly object CacheLock = new();
|
||||
|
|
@ -29,7 +31,7 @@ public static class WallpaperPathRetrieval
|
|||
var wallpaperPath = Win32Helper.GetWallpaperPath();
|
||||
if (string.IsNullOrEmpty(wallpaperPath) || !File.Exists(wallpaperPath))
|
||||
{
|
||||
App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Wallpaper path is invalid: {wallpaperPath}");
|
||||
App.API.LogInfo(ClassName, $"Wallpaper path is invalid: {wallpaperPath}");
|
||||
var wallpaperColor = GetWallpaperColor();
|
||||
return new SolidColorBrush(wallpaperColor);
|
||||
}
|
||||
|
|
@ -54,7 +56,7 @@ public static class WallpaperPathRetrieval
|
|||
|
||||
if (originalWidth == 0 || originalHeight == 0)
|
||||
{
|
||||
App.API.LogInfo(nameof(WallpaperPathRetrieval), $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
|
||||
App.API.LogInfo(ClassName, $"Failed to load bitmap: Width={originalWidth}, Height={originalHeight}");
|
||||
return new SolidColorBrush(Colors.Transparent);
|
||||
}
|
||||
|
||||
|
|
@ -95,7 +97,7 @@ public static class WallpaperPathRetrieval
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.API.LogException(nameof(WallpaperPathRetrieval), "Error retrieving wallpaper", ex);
|
||||
App.API.LogException(ClassName, "Error retrieving wallpaper", ex);
|
||||
return new SolidColorBrush(Colors.Transparent);
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +115,7 @@ public static class WallpaperPathRetrieval
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
App.API.LogException(nameof(WallpaperPathRetrieval), "Error parsing wallpaper color", ex);
|
||||
App.API.LogException(ClassName, "Error parsing wallpaper color", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@
|
|||
mc:Ignorable="d">
|
||||
<Button
|
||||
Width="Auto"
|
||||
Click="GetNewHotkey"
|
||||
FontSize="13"
|
||||
FontWeight="Bold"
|
||||
Foreground="{DynamicResource Color01B}"
|
||||
Click="GetNewHotkey">
|
||||
Foreground="{DynamicResource Color01B}">
|
||||
<Button.Template>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<Border
|
||||
x:Name="ButtonBorder"
|
||||
Padding="5,0,5,0"
|
||||
Padding="5 0 5 0"
|
||||
Background="{DynamicResource ButtonBackgroundColor}"
|
||||
BorderBrush="{DynamicResource ButtonInsideBorder}"
|
||||
BorderThickness="1"
|
||||
|
|
@ -28,26 +28,21 @@
|
|||
<Condition Property="IsMouseOver" Value="True" />
|
||||
<Condition Property="IsPressed" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="ButtonBorder" Property="Background"
|
||||
Value="{DynamicResource ButtonMousePressed}" />
|
||||
<Setter TargetName="ButtonBorder" Property="BorderBrush"
|
||||
Value="{DynamicResource ButtonMousePressedInsideBorder}" />
|
||||
<Setter TargetName="ButtonBorder" Property="Background" Value="{DynamicResource ButtonMousePressed}" />
|
||||
<Setter TargetName="ButtonBorder" Property="BorderBrush" Value="{DynamicResource ButtonMousePressedInsideBorder}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsMouseOver" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="ButtonBorder" Property="Background"
|
||||
Value="{DynamicResource ButtonMouseOver}" />
|
||||
<Setter TargetName="ButtonBorder" Property="Background" Value="{DynamicResource ButtonMouseOver}" />
|
||||
</MultiTrigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsPressed" Value="True" />
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter TargetName="ButtonBorder" Property="Background"
|
||||
Value="{DynamicResource ButtonMousePressed}" />
|
||||
<Setter TargetName="ButtonBorder" Property="BorderBrush"
|
||||
Value="{DynamicResource CustomContextHover}" />
|
||||
<Setter TargetName="ButtonBorder" Property="Background" Value="{DynamicResource ButtonMousePressed}" />
|
||||
<Setter TargetName="ButtonBorder" Property="BorderBrush" Value="{DynamicResource CustomContextHover}" />
|
||||
</MultiTrigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
|
|
@ -62,8 +57,8 @@
|
|||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Border
|
||||
Margin="2,5,2,5"
|
||||
Padding="10,5,10,5"
|
||||
Margin="2 5 2 5"
|
||||
Padding="10 5 10 5"
|
||||
Background="{DynamicResource AccentButtonBackground}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5">
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
#nullable enable
|
||||
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class HotkeyControl
|
||||
|
|
@ -65,7 +64,6 @@ namespace Flow.Launcher
|
|||
hotkeyControl.RefreshHotkeyInterface(hotkeyControl.Hotkey);
|
||||
}
|
||||
|
||||
|
||||
public static readonly DependencyProperty ChangeHotkeyProperty = DependencyProperty.Register(
|
||||
nameof(ChangeHotkey),
|
||||
typeof(ICommand),
|
||||
|
|
@ -79,7 +77,6 @@ namespace Flow.Launcher
|
|||
set { SetValue(ChangeHotkeyProperty, value); }
|
||||
}
|
||||
|
||||
|
||||
public static readonly DependencyProperty TypeProperty = DependencyProperty.Register(
|
||||
nameof(Type),
|
||||
typeof(HotkeyType),
|
||||
|
|
@ -103,6 +100,7 @@ namespace Flow.Launcher
|
|||
PreviewHotkey,
|
||||
OpenContextMenuHotkey,
|
||||
SettingWindowHotkey,
|
||||
OpenHistoryHotkey,
|
||||
CycleHistoryUpHotkey,
|
||||
CycleHistoryDownHotkey,
|
||||
SelectPrevPageHotkey,
|
||||
|
|
@ -133,6 +131,7 @@ namespace Flow.Launcher
|
|||
HotkeyType.PreviewHotkey => _settings.PreviewHotkey,
|
||||
HotkeyType.OpenContextMenuHotkey => _settings.OpenContextMenuHotkey,
|
||||
HotkeyType.SettingWindowHotkey => _settings.SettingWindowHotkey,
|
||||
HotkeyType.OpenHistoryHotkey => _settings.OpenHistoryHotkey,
|
||||
HotkeyType.CycleHistoryUpHotkey => _settings.CycleHistoryUpHotkey,
|
||||
HotkeyType.CycleHistoryDownHotkey => _settings.CycleHistoryDownHotkey,
|
||||
HotkeyType.SelectPrevPageHotkey => _settings.SelectPrevPageHotkey,
|
||||
|
|
@ -169,6 +168,9 @@ namespace Flow.Launcher
|
|||
case HotkeyType.SettingWindowHotkey:
|
||||
_settings.SettingWindowHotkey = value;
|
||||
break;
|
||||
case HotkeyType.OpenHistoryHotkey:
|
||||
_settings.OpenHistoryHotkey = value;
|
||||
break;
|
||||
case HotkeyType.CycleHistoryUpHotkey:
|
||||
_settings.CycleHistoryUpHotkey = value;
|
||||
break;
|
||||
|
|
@ -227,26 +229,29 @@ namespace Flow.Launcher
|
|||
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) =>
|
||||
hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
|
||||
|
||||
public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
|
||||
public string EmptyHotkey => App.API.GetTranslation("none");
|
||||
|
||||
public ObservableCollection<string> KeysToDisplay { get; set; } = new();
|
||||
|
||||
public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None);
|
||||
|
||||
|
||||
public void GetNewHotkey(object sender, RoutedEventArgs e)
|
||||
{
|
||||
OpenHotkeyDialog();
|
||||
_ = OpenHotkeyDialogAsync();
|
||||
}
|
||||
|
||||
private async Task OpenHotkeyDialog()
|
||||
private async Task OpenHotkeyDialogAsync()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Hotkey))
|
||||
{
|
||||
HotKeyMapper.RemoveHotkey(Hotkey);
|
||||
}
|
||||
|
||||
var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, WindowTitle);
|
||||
var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, WindowTitle)
|
||||
{
|
||||
Owner = Window.GetWindow(this)
|
||||
};
|
||||
|
||||
await dialog.ShowAsync();
|
||||
switch (dialog.ResultType)
|
||||
{
|
||||
|
|
@ -262,12 +267,11 @@ namespace Flow.Launcher
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true)
|
||||
{
|
||||
if (triggerValidate)
|
||||
{
|
||||
bool hotkeyAvailable = false;
|
||||
bool hotkeyAvailable;
|
||||
// TODO: This is a temporary way to enforce changing only the open flow hotkey to Win, and will be removed by PR #3157
|
||||
if (keyModel.ToString() == "LWin" || keyModel.ToString() == "RWin")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
xmlns:ui="http://schemas.modernwpf.com/2019"
|
||||
Background="{DynamicResource PopuBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0"
|
||||
BorderThickness="0 1 0 0"
|
||||
CornerRadius="8"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
Foreground="{DynamicResource PopupTextColor}"
|
||||
|
|
@ -24,14 +24,14 @@
|
|||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- Window title and the keys in the hotkey -->
|
||||
<Grid Grid.Row="0" Margin="26,12,26,0">
|
||||
<Grid Grid.Row="0" Margin="26 12 26 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
Margin="0,0,0,0"
|
||||
Margin="0 0 0 0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding WindowTitle}"
|
||||
|
|
@ -42,8 +42,8 @@
|
|||
Grid.Row="1"
|
||||
Width="450"
|
||||
Height="100"
|
||||
Margin="0,100,0,0"
|
||||
Padding="26,12,26,0">
|
||||
Margin="0 100 0 0"
|
||||
Padding="26 12 26 0">
|
||||
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||
<ItemsControl ItemsSource="{Binding KeysToDisplay}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
|
|
@ -56,12 +56,12 @@
|
|||
<Border
|
||||
MinWidth="50"
|
||||
MinHeight="50"
|
||||
Margin="5,0,5,0"
|
||||
Margin="5 0 5 0"
|
||||
Padding="8"
|
||||
Background="{DynamicResource AccentButtonBackground}"
|
||||
CornerRadius="6">
|
||||
<TextBlock
|
||||
Margin="5,0,5,0"
|
||||
Margin="5 0 5 0"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="18"
|
||||
|
|
@ -82,9 +82,9 @@
|
|||
<Border
|
||||
x:Name="Alert"
|
||||
Width="420"
|
||||
Padding="0, 10"
|
||||
VerticalAlignment="Center"
|
||||
Padding="0 10"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="{DynamicResource InfoBarWarningBG}"
|
||||
BorderBrush="{DynamicResource InfoBarBD}"
|
||||
BorderThickness="1"
|
||||
|
|
@ -97,21 +97,21 @@
|
|||
</Grid.ColumnDefinitions>
|
||||
<ui:FontIcon
|
||||
Grid.Column="0"
|
||||
Margin="20,0,14,0"
|
||||
Margin="20 0 14 0"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="15"
|
||||
Foreground="{DynamicResource InfoBarWarningIcon}"
|
||||
Glyph="" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
x:Name="tbMsg"
|
||||
Margin="0,0,0,2"
|
||||
Padding="0,0,8,0"
|
||||
Grid.Column="1"
|
||||
Margin="0 0 0 2"
|
||||
Padding="0 0 8 0"
|
||||
HorizontalAlignment="Left"
|
||||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="{DynamicResource Color05B}" />
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
TextWrapping="Wrap" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
|
|
@ -122,45 +122,45 @@
|
|||
Grid.Row="2"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0"
|
||||
BorderThickness="0 1 0 0"
|
||||
CornerRadius="0 0 8 8">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Margin="10 9 10 10"
|
||||
HorizontalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="OverwriteBtn"
|
||||
Height="30"
|
||||
MinHeight="36"
|
||||
MinWidth="100"
|
||||
Margin="0,0,4,0"
|
||||
Margin="0 0 4 0"
|
||||
Click="Overwrite"
|
||||
Content="{DynamicResource commonOverwrite}"
|
||||
Visibility="Collapsed"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
Style="{StaticResource AccentButtonStyle}"
|
||||
Visibility="Collapsed" />
|
||||
<Button
|
||||
x:Name="SaveBtn"
|
||||
Height="30"
|
||||
MinHeight="36"
|
||||
MinWidth="100"
|
||||
Margin="0,0,4,0"
|
||||
Margin="0 0 4 0"
|
||||
Click="Save"
|
||||
Content="{DynamicResource commonSave}"
|
||||
Style="{StaticResource AccentButtonStyle}" />
|
||||
<Button
|
||||
Height="30"
|
||||
MinHeight="36"
|
||||
MinWidth="100"
|
||||
Margin="4,0,4,0"
|
||||
Margin="4 0 4 0"
|
||||
Click="Reset"
|
||||
Content="{DynamicResource commonReset}" />
|
||||
<Button
|
||||
Height="30"
|
||||
MinHeight="36"
|
||||
MinWidth="100"
|
||||
Margin="4,0,4,0"
|
||||
Margin="4 0 4 0"
|
||||
Click="Delete"
|
||||
Content="{DynamicResource commonDelete}" />
|
||||
<Button
|
||||
Height="30"
|
||||
MinHeight="36"
|
||||
MinWidth="100"
|
||||
Margin="4,0,0,0"
|
||||
Margin="4 0 0 0"
|
||||
Click="Cancel"
|
||||
Content="{DynamicResource commonCancel}" />
|
||||
</StackPanel>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ using System.Windows;
|
|||
using System.Windows.Input;
|
||||
using ChefKeys;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
|
|
@ -34,7 +33,7 @@ public partial class HotkeyControlDialog : ContentDialog
|
|||
|
||||
public EResultType ResultType { get; private set; } = EResultType.Cancel;
|
||||
public string ResultValue { get; private set; } = string.Empty;
|
||||
public static string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none");
|
||||
public static string EmptyHotkey => App.API.GetTranslation("none");
|
||||
|
||||
private static bool isOpenFlowHotkey;
|
||||
|
||||
|
|
@ -42,7 +41,7 @@ public partial class HotkeyControlDialog : ContentDialog
|
|||
{
|
||||
WindowTitle = windowTitle switch
|
||||
{
|
||||
"" or null => InternationalizationManager.Instance.GetTranslation("hotkeyRegTitle"),
|
||||
"" or null => App.API.GetTranslation("hotkeyRegTitle"),
|
||||
_ => windowTitle
|
||||
};
|
||||
DefaultHotkey = defaultHotkey;
|
||||
|
|
@ -141,14 +140,14 @@ public partial class HotkeyControlDialog : ContentDialog
|
|||
if (_hotkeySettings.RegisteredHotkeys.FirstOrDefault(v => v.Hotkey == hotkey) is { } registeredHotkeyData)
|
||||
{
|
||||
var description = string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation(registeredHotkeyData.DescriptionResourceKey),
|
||||
App.API.GetTranslation(registeredHotkeyData.DescriptionResourceKey),
|
||||
registeredHotkeyData.DescriptionFormatVariables
|
||||
);
|
||||
Alert.Visibility = Visibility.Visible;
|
||||
if (registeredHotkeyData.RemoveHotkey is not null)
|
||||
{
|
||||
tbMsg.Text = string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableEditable"),
|
||||
App.API.GetTranslation("hotkeyUnavailableEditable"),
|
||||
description
|
||||
);
|
||||
SaveBtn.IsEnabled = false;
|
||||
|
|
@ -160,7 +159,7 @@ public partial class HotkeyControlDialog : ContentDialog
|
|||
else
|
||||
{
|
||||
tbMsg.Text = string.Format(
|
||||
InternationalizationManager.Instance.GetTranslation("hotkeyUnavailableUneditable"),
|
||||
App.API.GetTranslation("hotkeyUnavailableUneditable"),
|
||||
description
|
||||
);
|
||||
SaveBtn.IsEnabled = false;
|
||||
|
|
@ -176,7 +175,7 @@ public partial class HotkeyControlDialog : ContentDialog
|
|||
|
||||
if (!CheckHotkeyAvailability(hotkey.Value, true))
|
||||
{
|
||||
tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable");
|
||||
tbMsg.Text = App.API.GetTranslation("hotkeyUnavailable");
|
||||
Alert.Visibility = Visibility.Visible;
|
||||
SaveBtn.IsEnabled = false;
|
||||
SaveBtn.Visibility = Visibility.Visible;
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 KiB |
|
|
@ -7,6 +7,11 @@
|
|||
انقر فوق لا إذا كان مثبتاً بالفعل، وسوف يطلب منك تحديد المجلد الذي يحتوي على {1} القابل للتنفيذ
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">الرجاء اختيار الملف التنفيذي لـ {0}</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
Your selected {0} executable is invalid.
|
||||
{2}{2}
|
||||
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">تعذر تعيين مسار الملف التنفيذي لـ {0}، يرجى المحاولة من إعدادات Flow (قم بالتمرير إلى الأسفل).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">فشل في تهيئة الإضافات</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">الإضافات: {0} - فشل في التحميل وسيتم تعطيلها، يرجى الاتصال بمطور الإضافة للحصول على المساعدة</system:String>
|
||||
|
|
@ -37,7 +42,8 @@
|
|||
<system:String x:Key="GameMode">وضع اللعب</system:String>
|
||||
<system:String x:Key="GameModeToolTip">تعليق استخدام مفاتيح التشغيل السريع.</system:String>
|
||||
<system:String x:Key="PositionReset">إعادة تعيين الموقع</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">إعادة تعيين موضع نافذة البحث</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Type here to search</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">الإعدادات</system:String>
|
||||
|
|
@ -50,7 +56,7 @@
|
|||
<system:String x:Key="setAutoStartFailed">خطأ في إعداد التشغيل عند بدء التشغيل</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">إخفاء Flow Launcher عند فقدان التركيز</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">عدم عرض إشعارات الإصدار الجديد</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">موضع نافذة البحث</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">تذكر آخر موقع</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">الشاشة مع مؤشر الماوس</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">الشاشة مع النافذة المركزة</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">تفريغ الاستعلام الأخير</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
|
||||
<system:String x:Key="KeepMaxResults">ارتفاع ثابت للنافذة</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">ارتفاع النافذة غير قابل للتعديل عن طريق السحب.</system:String>
|
||||
<system:String x:Key="maxShowResults">الحد الأقصى للنتائج المعروضة</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">يمكنك أيضًا تعديل هذا بسرعة باستخدام CTRL+Plus وCTRL+Minus.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">تجاهل مفاتيح التشغيل السريع في وضع ملء الشاشة</system:String>
|
||||
|
|
@ -102,6 +106,36 @@
|
|||
<system:String x:Key="AlwaysPreview">دائمًا معاينة</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">فتح لوحة المعاينة دائمًا عند تنشيط Flow. اضغط على {0} للتبديل بين المعاينة وعدمها.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">تأثير الظل غير مسموح به بينما يتم تمكين تأثير التمويه في السمة الحالية</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Wait time before showing results after typing stops. Higher values wait longer. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
|
||||
|
||||
If you experience any problems, you may need to enable "Use previous version of Korean IME".
|
||||
|
||||
|
||||
Open Setting in Windows 11 and go to:
|
||||
|
||||
Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,
|
||||
|
||||
and enable "Use previous version of Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Open Language and Region System Settings</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">فتح</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Use Previous Korean IME</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
|
||||
<system:String x:Key="homePage">Home Page</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">البحث عن إضافة</system:String>
|
||||
|
|
@ -118,6 +152,13 @@
|
|||
<system:String x:Key="currentActionKeywords">كلمة الفعل الحالية</system:String>
|
||||
<system:String x:Key="newActionKeyword">كلمة فعل جديدة</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">تغيير كلمات الفعل</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Advanced Settings:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">مفع</system:String>
|
||||
<system:String x:Key="DisplayModePriority">الأولوي</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Home Page</system:String>
|
||||
<system:String x:Key="currentPriority">الأولوية الحالية</system:String>
|
||||
<system:String x:Key="newPriority">أولوية جديدة</system:String>
|
||||
<system:String x:Key="priority">الأولوية</system:String>
|
||||
|
|
@ -131,6 +172,8 @@
|
|||
<system:String x:Key="plugin_uninstall">إلغاء التثبيت</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">متجر الإضافات</system:String>
|
||||
|
|
@ -167,6 +210,9 @@
|
|||
<system:String x:Key="resultItemFont">خط عنوان النتيجة</system:String>
|
||||
<system:String x:Key="resultSubItemFont">خط العنوان الفرعي للنتيجة</system:String>
|
||||
<system:String x:Key="resetCustomize">إعادة التعيين</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">تخصيص</system:String>
|
||||
<system:String x:Key="windowMode">وضع النافذة</system:String>
|
||||
<system:String x:Key="opacity">الشفافية</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">مخصص</system:String>
|
||||
<system:String x:Key="Clock">الساعة</system:String>
|
||||
<system:String x:Key="Date">التاريخ</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">هذه السمة تدعم الوضعين (فاتح/داكن).</system:String>
|
||||
<system:String x:Key="BackdropType">Backdrop Type</system:String>
|
||||
<system:String x:Key="BackdropInfo">The backdrop effect is not applied in the preview.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">بلا</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two (light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">هذه السمة تدعم الخلفية الضبابية الشفافة.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||
<system:String x:Key="PlaceholderText">Placeholder text</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Size</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window size is not adjustable by dragging.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">مفتاح الاختصار</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">استخدام أيقونات Segoe Fluent</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">استخدام أيقونات Segoe Fluent لنتائج الاستعلام حيثما كان مدعومًا</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">اضغط على المفتاح</system:String>
|
||||
<system:String x:Key="showBadges">Show Result Badges</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">For supported plugins, badges are displayed to help distinguish them more easily.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">بروكسي HTTP</system:String>
|
||||
|
|
@ -294,16 +356,23 @@
|
|||
<system:String x:Key="logfolder">مجلد السجلات</system:String>
|
||||
<system:String x:Key="clearlogfolder">مسح السجلات</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">هل أنت متأكد أنك تريد حذف جميع السجلات؟</system:String>
|
||||
<system:String x:Key="cachefolder">Cache Folder</system:String>
|
||||
<system:String x:Key="clearcachefolder">Clear Caches</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||
<system:String x:Key="welcomewindow">معالج الترحيب</system:String>
|
||||
<system:String x:Key="userdatapath">موقع بيانات المستخدم</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">يتم حفظ إعدادات المستخدم والإضافات المثبتة في مجلد بيانات المستخدم. قد يختلف هذا الموقع اعتمادًا على ما إذا كان في وضع النقل أم لا.</system:String>
|
||||
<system:String x:Key="userdatapathButton">فتح المجلد</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">اختر مدير الملفات</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Learn more</system:String>
|
||||
<system:String x:Key="fileManager_tips">يرجى تحديد موقع ملف مدير الملفات الذي تستخدمه وإضافة الحجج حسب الحاجة. يمثل "%d" مسار الدليل المفتوح، ويستخدمه الحقل "الحجة للمجلد" للأوامر التي تفتح أدلة محددة. يمثل "%f" مسار الملف المفتوح، ويستخدمه الحقل "الحجة للملف" للأوامر التي تفتح ملفات محددة.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">على سبيل المثال، إذا كان مدير الملفات يستخدم أمرًا مثل "totalcmd.exe /A c:\windows" لفتح دليل c:\windows، فإن مسار مدير الملفات سيكون totalcmd.exe، وحجة المجلد ستكون /A "%d". قد تحتاج بعض مديري الملفات مثل QTTabBar فقط إلى توفير مسار، في هذه الحالة استخدم "%d" كمسار مدير الملفات واترك باقي الحقول فارغة.</system:String>
|
||||
<system:String x:Key="fileManager_name">مدير الملفات</system:String>
|
||||
|
|
@ -311,6 +380,8 @@
|
|||
<system:String x:Key="fileManager_path">مسار مدير الملفات</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">حجة للمجلد</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">حجة للملف</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">متصفح الويب الافتراضي</system:String>
|
||||
|
|
@ -335,9 +406,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">لا يمكن العثور على الإضافة المحددة</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">كلمة المفتاح الجديدة لا يمكن أن تكون فارغة</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">تم تعيين كلمة المفتاح الجديدة هذه إلى إضافة أخرى، يرجى اختيار كلمة أخرى</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
|
||||
<system:String x:Key="success">نجاح</system:String>
|
||||
<system:String x:Key="completedSuccessfully">اكتمل بنجاح</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">أدخل كلمة المفتاح التي ترغب في استخدامها لبدء الإضافة. استخدم * إذا كنت لا ترغب في تحديد أي كلمة، وسيتم تشغيل الإضافة بدون كلمات مفتاحية.</system:String>
|
||||
<system:String x:Key="failedToCopy">Failed to copy</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Home Page</system:String>
|
||||
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">مفتاح اختصار الاستعلام المخصص</system:String>
|
||||
|
|
@ -392,6 +473,14 @@
|
|||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">خطأ</system:String>
|
||||
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">يرجى الانتظار...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
Your selected {0} executable is invalid.
|
||||
{2}{2}
|
||||
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>
|
||||
|
|
@ -37,7 +42,8 @@
|
|||
<system:String x:Key="GameMode">Herní režim</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Potlačit užívání klávesových zkratek.</system:String>
|
||||
<system:String x:Key="PositionReset">Obnovit pozici</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Obnovit pozici vyhledávacího okna</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Type here to search</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Nastavení</system:String>
|
||||
|
|
@ -50,7 +56,7 @@
|
|||
<system:String x:Key="setAutoStartFailed">Při nastavování spouštění došlo k chybě</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Skrýt Flow Launcher při vykliknutí</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Nezobrazovat oznámení o nové verzi</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Pozice vyhledávacího okna</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Zapamatovat poslední pozici</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Obrazovka s kurzorem</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Obrazovka s aktivním oknem</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">Smazat poslední dotaz</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Height</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window height is not adjustable by dragging.</system:String>
|
||||
<system:String x:Key="maxShowResults">Počet zobrazených výsledků</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">Toto nastavení můžete také rychle upravit pomocí CTRL + Plus a CTRL + Minus.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorovat klávesové zkratky v režimu celé obrazovky</system:String>
|
||||
|
|
@ -102,6 +106,36 @@
|
|||
<system:String x:Key="AlwaysPreview">Vždy zobrazit náhled</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Při aktivaci služby Flow vždy otevřete panel náhledu. Stisknutím klávesy {0} přepnete náhled.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">Stínový efekt není povolen, pokud je aktivní efekt rozostření</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Wait time before showing results after typing stops. Higher values wait longer. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
|
||||
|
||||
If you experience any problems, you may need to enable "Use previous version of Korean IME".
|
||||
|
||||
|
||||
Open Setting in Windows 11 and go to:
|
||||
|
||||
Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,
|
||||
|
||||
and enable "Use previous version of Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Open Language and Region System Settings</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Otevřít</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Use Previous Korean IME</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
|
||||
<system:String x:Key="homePage">Home Page</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Vyhledat plugin</system:String>
|
||||
|
|
@ -118,6 +152,13 @@
|
|||
<system:String x:Key="currentActionKeywords">Aktuální aktivační příkaz</system:String>
|
||||
<system:String x:Key="newActionKeyword">Nový aktivační příkaz</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Upravit aktivační příkaz</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Advanced Settings:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Povoleno</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Priorita</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Home Page</system:String>
|
||||
<system:String x:Key="currentPriority">Aktuální priorita</system:String>
|
||||
<system:String x:Key="newPriority">Nová priorita</system:String>
|
||||
<system:String x:Key="priority">Priorita</system:String>
|
||||
|
|
@ -131,6 +172,8 @@
|
|||
<system:String x:Key="plugin_uninstall">Odinstalovat</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Obchod s pluginy</system:String>
|
||||
|
|
@ -167,6 +210,9 @@
|
|||
<system:String x:Key="resultItemFont">Result Title Font</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Result Subtitle Font</system:String>
|
||||
<system:String x:Key="resetCustomize">Reset</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Customize</system:String>
|
||||
<system:String x:Key="windowMode">Režim okna</system:String>
|
||||
<system:String x:Key="opacity">Neprůhlednost</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">Vlastní</system:String>
|
||||
<system:String x:Key="Clock">Hodiny</system:String>
|
||||
<system:String x:Key="Date">Datum</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
||||
<system:String x:Key="BackdropType">Backdrop Type</system:String>
|
||||
<system:String x:Key="BackdropInfo">The backdrop effect is not applied in the preview.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">None</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two (light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||
<system:String x:Key="PlaceholderText">Placeholder text</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Size</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window size is not adjustable by dragging.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Klávesová zkratka</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">Použít ikony Segoe Fluent</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Použití ikon Segoe Fluent, pokud jsou podporovány</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Stiskněte klávesu</system:String>
|
||||
<system:String x:Key="showBadges">Show Result Badges</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">For supported plugins, badges are displayed to help distinguish them more easily.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -294,16 +356,23 @@
|
|||
<system:String x:Key="logfolder">Složka s logy</system:String>
|
||||
<system:String x:Key="clearlogfolder">Vymazat logy</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">Opravdu chcete odstranit všechny logy?</system:String>
|
||||
<system:String x:Key="cachefolder">Cache Folder</system:String>
|
||||
<system:String x:Key="clearcachefolder">Clear Caches</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||
<system:String x:Key="welcomewindow">Průvodce</system:String>
|
||||
<system:String x:Key="userdatapath">User Data Location</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Open Folder</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Vybrat správce souborů</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Learn more</system:String>
|
||||
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments as required. The "%d" represents the directory path to open for, used by the Arg for Folder field and for commands opening specific directories. The "%f" represents the file path to open for, used by the Arg for File field and for commands opening specific files.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">For example, if the file manager uses a command such as "totalcmd.exe /A c:\windows" to open the c:\windows directory, the File Manager Path will be totalcmd.exe, and the Arg For Folder will be /A "%d". Certain file managers like QTTabBar may just require a path to be supplied, in this instance use "%d" as the File Manager Path and leave the rest of the fileds blank.</system:String>
|
||||
<system:String x:Key="fileManager_name">Správce souborů</system:String>
|
||||
|
|
@ -311,6 +380,8 @@
|
|||
<system:String x:Key="fileManager_path">Cesta k správci souborů</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Argumenty pro složku</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Argumenty pro Soubor</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Výchozí prohlížeč</system:String>
|
||||
|
|
@ -335,9 +406,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">Nepodařilo se najít zadaný plugin</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">Nový aktivační příkaz nemůže být prázdný</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">Nový aktivační příkaz byl již přiřazen jinému pluginu, vyberte jiný aktivační příkaz</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
|
||||
<system:String x:Key="success">Úspěšné</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Úspěšně dokončeno</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Zadejte aktivační příkaz, který je nutný ke spuštění pluginu. Pokud nechcete zadávat aktivační příkaz, použijte * a plugin bude spuštěn bez aktivačního příkazu.</system:String>
|
||||
<system:String x:Key="failedToCopy">Failed to copy</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Home Page</system:String>
|
||||
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Vlastní klávesová zkratka pro vyhledávání</system:String>
|
||||
|
|
@ -392,6 +473,14 @@ Pokud před zkratku při zadávání přidáte znak "@", bude odpovíd
|
|||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Chyba</system:String>
|
||||
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Počkejte prosím...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
Your selected {0} executable is invalid.
|
||||
{2}{2}
|
||||
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>
|
||||
|
|
@ -25,32 +30,33 @@
|
|||
<system:String x:Key="iconTraySettings">Indstillinger</system:String>
|
||||
<system:String x:Key="iconTrayAbout">Om</system:String>
|
||||
<system:String x:Key="iconTrayExit">Afslut</system:String>
|
||||
<system:String x:Key="closeWindow">Close</system:String>
|
||||
<system:String x:Key="closeWindow">Luk</system:String>
|
||||
<system:String x:Key="copy">Copy</system:String>
|
||||
<system:String x:Key="cut">Cut</system:String>
|
||||
<system:String x:Key="paste">Paste</system:String>
|
||||
<system:String x:Key="cut">Klip</system:String>
|
||||
<system:String x:Key="paste">Indsæt</system:String>
|
||||
<system:String x:Key="undo">Undo</system:String>
|
||||
<system:String x:Key="selectAll">Select All</system:String>
|
||||
<system:String x:Key="fileTitle">File</system:String>
|
||||
<system:String x:Key="folderTitle">Folder</system:String>
|
||||
<system:String x:Key="textTitle">Text</system:String>
|
||||
<system:String x:Key="GameMode">Game Mode</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Suspend the use of Hotkeys.</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Suspender brugen af genvejstaster.</system:String>
|
||||
<system:String x:Key="PositionReset">Position Reset</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Type here to search</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Indstillinger</system:String>
|
||||
<system:String x:Key="general">Generelt</system:String>
|
||||
<system:String x:Key="portableMode">Portable Mode</system:String>
|
||||
<system:String x:Key="portableModeToolTIp">Store all settings and user data in one folder (Useful when used with removable drives or cloud services).</system:String>
|
||||
<system:String x:Key="portableModeToolTIp">Gem alle indstillinger og brugerdata i én mappe (nyttigt ved brug af flytbare drev eller cloud-tjenester).</system:String>
|
||||
<system:String x:Key="startFlowLauncherOnSystemStartup">Start Flow Launcher ved system start</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
|
||||
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Skjul Flow Launcher ved mistet fokus</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Vis ikke notifikationer om nye versioner</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Remember Last Position</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Monitor with Mouse Cursor</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Monitor with Focused Window</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">Empty last Query</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Height</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window height is not adjustable by dragging.</system:String>
|
||||
<system:String x:Key="maxShowResults">Maksimum antal resultater vist</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">You can also quickly adjust this by using CTRL+Plus and CTRL+Minus.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorer genvejstaster i fuldskærmsmode</system:String>
|
||||
|
|
@ -101,7 +105,37 @@
|
|||
<system:String x:Key="ShouldUsePinyinToolTip">Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese.</system:String>
|
||||
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">Skyggeeffekt er ikke tilladt, når det aktuelle tema har sløringseffekt aktiveret</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Wait time before showing results after typing stops. Higher values wait longer. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
|
||||
|
||||
If you experience any problems, you may need to enable "Use previous version of Korean IME".
|
||||
|
||||
|
||||
Open Setting in Windows 11 and go to:
|
||||
|
||||
Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,
|
||||
|
||||
and enable "Use previous version of Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Open Language and Region System Settings</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Åben</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Use Previous Korean IME</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
|
||||
<system:String x:Key="homePage">Home Page</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
|
|
@ -111,35 +145,44 @@
|
|||
<system:String x:Key="plugin">Plugin</system:String>
|
||||
<system:String x:Key="plugins">Plugins</system:String>
|
||||
<system:String x:Key="browserMorePlugins">Find flere plugins</system:String>
|
||||
<system:String x:Key="enable">On</system:String>
|
||||
<system:String x:Key="enable">Til</system:String>
|
||||
<system:String x:Key="disable">Deaktiver</system:String>
|
||||
<system:String x:Key="actionKeywordsTitle">Action keyword Setting</system:String>
|
||||
<system:String x:Key="actionKeywords">Nøgleord</system:String>
|
||||
<system:String x:Key="currentActionKeywords">Current action keyword</system:String>
|
||||
<system:String x:Key="newActionKeyword">New action keyword</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Change Action Keywords</system:String>
|
||||
<system:String x:Key="currentPriority">Current Priority</system:String>
|
||||
<system:String x:Key="newPriority">New Priority</system:String>
|
||||
<system:String x:Key="priority">Priority</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Advanced Settings:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Enabled</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Prioritet</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Home Page</system:String>
|
||||
<system:String x:Key="currentPriority">Nuværende prioritet</system:String>
|
||||
<system:String x:Key="newPriority">Ny prioritet</system:String>
|
||||
<system:String x:Key="priority">Prioritet</system:String>
|
||||
<system:String x:Key="priorityToolTip">Change Plugin Results Priority</system:String>
|
||||
<system:String x:Key="pluginDirectory">Plugin bibliotek</system:String>
|
||||
<system:String x:Key="author">af</system:String>
|
||||
<system:String x:Key="plugin_init_time">Initaliseringstid:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Søgetid:</system:String>
|
||||
<system:String x:Key="plugin_query_version">Version</system:String>
|
||||
<system:String x:Key="plugin_query_web">Website</system:String>
|
||||
<system:String x:Key="plugin_query_web">Hjemmeside</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plugin Store</system:String>
|
||||
<system:String x:Key="pluginStore">Plugin-butik</system:String>
|
||||
<system:String x:Key="pluginStore_NewRelease">New Release</system:String>
|
||||
<system:String x:Key="pluginStore_RecentlyUpdated">Recently Updated</system:String>
|
||||
<system:String x:Key="pluginStore_None">Plugins</system:String>
|
||||
<system:String x:Key="pluginStore_Installed">Installed</system:String>
|
||||
<system:String x:Key="refresh">Refresh</system:String>
|
||||
<system:String x:Key="installbtn">Install</system:String>
|
||||
<system:String x:Key="installbtn">Installer</system:String>
|
||||
<system:String x:Key="uninstallbtn">Uninstall</system:String>
|
||||
<system:String x:Key="updatebtn">Opdater</system:String>
|
||||
<system:String x:Key="LabelInstalledToolTip">Plugin already installed</system:String>
|
||||
|
|
@ -151,8 +194,8 @@
|
|||
<system:String x:Key="theme">Tema</system:String>
|
||||
<system:String x:Key="appearance">Appearance</system:String>
|
||||
<system:String x:Key="browserMoreThemes">Søg efter flere temaer</system:String>
|
||||
<system:String x:Key="howToCreateTheme">How to create a theme</system:String>
|
||||
<system:String x:Key="hiThere">Hi There</system:String>
|
||||
<system:String x:Key="howToCreateTheme">Hvordan man opretter et tema</system:String>
|
||||
<system:String x:Key="hiThere">Hejsa</system:String>
|
||||
<system:String x:Key="SampleTitleExplorer">Explorer</system:String>
|
||||
<system:String x:Key="SampleSubTitleExplorer">Search for files, folders and file contents</system:String>
|
||||
<system:String x:Key="SampleTitleWebSearch">WebSearch</system:String>
|
||||
|
|
@ -167,18 +210,21 @@
|
|||
<system:String x:Key="resultItemFont">Result Title Font</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Result Subtitle Font</system:String>
|
||||
<system:String x:Key="resetCustomize">Reset</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Customize</system:String>
|
||||
<system:String x:Key="windowMode">Vindue mode</system:String>
|
||||
<system:String x:Key="opacity">Gennemsigtighed</system:String>
|
||||
<system:String x:Key="theme_load_failure_path_not_exists">Theme {0} not exists, fallback to default theme</system:String>
|
||||
<system:String x:Key="theme_load_failure_parse_error">Fail to load theme {0}, fallback to default theme</system:String>
|
||||
<system:String x:Key="ThemeFolder">Theme Folder</system:String>
|
||||
<system:String x:Key="OpenThemeFolder">Open Theme Folder</system:String>
|
||||
<system:String x:Key="ColorScheme">Color Scheme</system:String>
|
||||
<system:String x:Key="ColorSchemeSystem">System Default</system:String>
|
||||
<system:String x:Key="ColorSchemeLight">Light</system:String>
|
||||
<system:String x:Key="ColorSchemeDark">Dark</system:String>
|
||||
<system:String x:Key="SoundEffect">Sound Effect</system:String>
|
||||
<system:String x:Key="theme_load_failure_path_not_exists">Temaet {0} findes ikke. Falder tilbage til standardtema</system:String>
|
||||
<system:String x:Key="theme_load_failure_parse_error">Kunne ikke indlæse temaet {0}. Falder tilbage til standardtema</system:String>
|
||||
<system:String x:Key="ThemeFolder">Temamappe</system:String>
|
||||
<system:String x:Key="OpenThemeFolder">Åbn temamappe</system:String>
|
||||
<system:String x:Key="ColorScheme">Farveskema</system:String>
|
||||
<system:String x:Key="ColorSchemeSystem">Systemstandard</system:String>
|
||||
<system:String x:Key="ColorSchemeLight">Lys</system:String>
|
||||
<system:String x:Key="ColorSchemeDark">Mørk</system:String>
|
||||
<system:String x:Key="SoundEffect">Lydeffekt</system:String>
|
||||
<system:String x:Key="SoundEffectTip">Play a small sound when the search window opens</system:String>
|
||||
<system:String x:Key="SoundEffectVolume">Sound Effect Volume</system:String>
|
||||
<system:String x:Key="SoundEffectVolumeTip">Adjust the volume of the sound effect</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">Custom</system:String>
|
||||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
||||
<system:String x:Key="BackdropType">Backdrop Type</system:String>
|
||||
<system:String x:Key="BackdropInfo">The backdrop effect is not applied in the preview.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">None</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two (light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||
<system:String x:Key="PlaceholderText">Placeholder text</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Size</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window size is not adjustable by dragging.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Genvejstast</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">Use Segoe Fluent Icons</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Use Segoe Fluent Icons for query results where supported</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Press Key</system:String>
|
||||
<system:String x:Key="showBadges">Show Result Badges</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">For supported plugins, badges are displayed to help distinguish them more easily.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -273,7 +335,7 @@
|
|||
|
||||
<!-- Setting About -->
|
||||
<system:String x:Key="about">Om</system:String>
|
||||
<system:String x:Key="website">Website</system:String>
|
||||
<system:String x:Key="website">Hjemmeside</system:String>
|
||||
<system:String x:Key="github">GitHub</system:String>
|
||||
<system:String x:Key="docs">Docs</system:String>
|
||||
<system:String x:Key="version">Version</system:String>
|
||||
|
|
@ -294,36 +356,45 @@
|
|||
<system:String x:Key="logfolder">Log Folder</system:String>
|
||||
<system:String x:Key="clearlogfolder">Clear Logs</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">Are you sure you want to delete all logs?</system:String>
|
||||
<system:String x:Key="cachefolder">Cache Folder</system:String>
|
||||
<system:String x:Key="clearcachefolder">Clear Caches</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||
<system:String x:Key="welcomewindow">Wizard</system:String>
|
||||
<system:String x:Key="userdatapath">User Data Location</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Open Folder</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Select File Manager</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Learn more</system:String>
|
||||
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments as required. The "%d" represents the directory path to open for, used by the Arg for Folder field and for commands opening specific directories. The "%f" represents the file path to open for, used by the Arg for File field and for commands opening specific files.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">For example, if the file manager uses a command such as "totalcmd.exe /A c:\windows" to open the c:\windows directory, the File Manager Path will be totalcmd.exe, and the Arg For Folder will be /A "%d". Certain file managers like QTTabBar may just require a path to be supplied, in this instance use "%d" as the File Manager Path and leave the rest of the fileds blank.</system:String>
|
||||
<system:String x:Key="fileManager_name">File Manager</system:String>
|
||||
<system:String x:Key="fileManager_profile_name">Profile Name</system:String>
|
||||
<system:String x:Key="fileManager_path">File Manager Path</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Arg For Folder</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
|
||||
<system:String x:Key="fileManager_name">Filhåndtering</system:String>
|
||||
<system:String x:Key="fileManager_profile_name">Profilnavn</system:String>
|
||||
<system:String x:Key="fileManager_path">Sti til filhåndtering</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Arg for mappe</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Arg for fil</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
|
||||
<system:String x:Key="defaultBrowser_tips">The default setting follows the OS default browser setting. If specified separately, flow uses that browser.</system:String>
|
||||
<system:String x:Key="defaultBrowser_name">Browser</system:String>
|
||||
<system:String x:Key="defaultBrowser_profile_name">Browser Name</system:String>
|
||||
<system:String x:Key="defaultBrowser_path">Browser Path</system:String>
|
||||
<system:String x:Key="defaultBrowser_path">Sti til browser</system:String>
|
||||
<system:String x:Key="defaultBrowser_newWindow">New Window</system:String>
|
||||
<system:String x:Key="defaultBrowser_newTab">New Tab</system:String>
|
||||
<system:String x:Key="defaultBrowser_parameter">Private Mode</system:String>
|
||||
<system:String x:Key="defaultBrowser_parameter">Privattilstand</system:String>
|
||||
|
||||
<!-- Priority Setting Dialog -->
|
||||
<system:String x:Key="changePriorityWindow">Change Priority</system:String>
|
||||
<system:String x:Key="changePriorityWindow">Skift prioritet</system:String>
|
||||
<system:String x:Key="priority_tips">Greater the number, the higher the result will be ranked. Try setting it as 5. If you want the results to be lower than any other plugin's, provide a negative number</system:String>
|
||||
<system:String x:Key="invalidPriority">Please provide an valid integer for Priority!</system:String>
|
||||
|
||||
|
|
@ -335,9 +406,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">Kan ikke finde det valgte plugin</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">Nyt nøgleord må ikke være tomt</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">Nyt nøgleord er tilknyttet et andet plugin, tilknyt venligst et andet nyt nøgeleord</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
|
||||
<system:String x:Key="success">Fortsæt</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Brug * hvis du ikke vil angive et nøgleord</system:String>
|
||||
<system:String x:Key="failedToCopy">Failed to copy</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Home Page</system:String>
|
||||
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Tilpasset søgegenvejstast</system:String>
|
||||
|
|
@ -392,6 +473,14 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in
|
|||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Error</system:String>
|
||||
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Please wait...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,13 +7,18 @@
|
|||
Klicken Sie auf „Nein“, wenn es bereits installiert ist, und Sie werden aufgefordert, den Ordner auszuwählen, der die ausführbare Datei {1} enthält
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Bitte wählen Sie die ausführbare Datei {0} aus</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
Your selected {0} executable is invalid.
|
||||
{2}{2}
|
||||
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Der Pfad zur ausführbaren Datei {0} kann nicht festgelegt werden. Bitte versuchen Sie es in den Einstellungen von Flow (scrollen Sie nach unten).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">Plug-ins können nicht initialisiert werden</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Plug-ins: {0} - nicht geladen werden und wird deaktiviert, bitte kontaktiere Sie den Ersteller des Plug-ins für Hilfe</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Plug-ins: {0} - nicht geladen werden und wird deaktiviert, bitte kontaktieren Sie den Ersteller des Plug-ins für Hilfe</system:String>
|
||||
|
||||
<!-- MainWindow -->
|
||||
<system:String x:Key="registerHotkeyFailed">Hotkey "{0}" konnte nicht registriert werden. Der Hotkey ist möglicherweise von einem anderen Programm in Verwendung. Wechseln Sie zu einem anderen Hotkey oder beenden Sie das andere Programm.</system:String>
|
||||
<system:String x:Key="unregisterHotkeyFailed">Failed to unregister hotkey "{0}". Please try again or see log for details</system:String>
|
||||
<system:String x:Key="unregisterHotkeyFailed">Registrierung des Hotkeys "{0}" konnte nicht aufgehoben werden. Bitte versuchen Sie es erneut oder lesen Sie das Log für Details</system:String>
|
||||
<system:String x:Key="MessageBoxTitle">Flow Launcher</system:String>
|
||||
<system:String x:Key="couldnotStartCmd">Konnte nicht gestartet werden {0}</system:String>
|
||||
<system:String x:Key="invalidFlowLauncherPluginFileFormat">Flow Launcher Plug-in-Dateiformat ungültig</system:String>
|
||||
|
|
@ -37,7 +42,8 @@
|
|||
<system:String x:Key="GameMode">Spielmodus</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Aussetzen der Verwendung von Hotkeys.</system:String>
|
||||
<system:String x:Key="PositionReset">Position zurücksetzen</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Position des Suchfensters zurücksetze</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Position des Suchfensters zurücksetzen</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Zum Suchen hier tippen</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Einstellungen</system:String>
|
||||
|
|
@ -45,12 +51,12 @@
|
|||
<system:String x:Key="portableMode">Portabler Modus</system:String>
|
||||
<system:String x:Key="portableModeToolTIp">Speichern Sie alle Einstellungen und Benutzerdaten in einem Ordner (nützlich bei Verwendung von Wechsellaufwerken oder Cloud-Diensten).</system:String>
|
||||
<system:String x:Key="startFlowLauncherOnSystemStartup">Flow Launcher bei Systemstart starten</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartup">Use logon task instead of startup entry for faster startup experience</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartupTooltip">After uninstallation, you need to manually remove this task (Flow.Launcher Startup) via Task Scheduler</system:String>
|
||||
<system:String x:Key="setAutoStartFailed">Fehler bei Einstellungsstart bei Start</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartup">Log-on-Aufgabe anstelle des Starteintrags für schnelleres Startup-Erfahrung verwenden</system:String>
|
||||
<system:String x:Key="useLogonTaskForStartupTooltip">Nach der Deinstallation müssen Sie diese Aufgabe (Flow.Launcher Startup) via Task-Scheduler manuell entfernen</system:String>
|
||||
<system:String x:Key="setAutoStartFailed">Fehler bei Einstellungsstart beim Start</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Flow Launcher ausblenden, wenn Fokus verloren geht</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Versionsbenachrichtigungen nicht zeigen</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Position des Suchfensters</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Letzte Position merken</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Monitor mit Mauscursor</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Monitor mit fokussiertem Fenster</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">Letzte Abfrage leeren</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Letztes Aktions-Schlüsselwort beibehalten</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Letztes Aktions-Schlüsselwort auswählen</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Feste Fensterhöhe</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">Die Fensterhöhe ist durch Ziehen nicht anpassbar.</system:String>
|
||||
<system:String x:Key="maxShowResults">Maximal gezeigte Ergebnisse</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">Sie können dies auch unter Verwendung von STRG+Plus und STRG+Minus schnell anpassen.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Hotkeys im Vollbildmodus ignorieren</system:String>
|
||||
|
|
@ -102,6 +106,36 @@
|
|||
<system:String x:Key="AlwaysPreview">Immer Vorschau</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Vorschau-Panel immer öffnen, wenn Flow aktiviert ist. Drücken Sie {0}, um Vorschau umzuschalten.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">Schatteneffekt ist nicht erlaubt, während das aktuelle Theme den Unschärfe-Effekt aktiviert hat</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Wait time before showing results after typing stops. Higher values wait longer. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
|
||||
|
||||
If you experience any problems, you may need to enable "Use previous version of Korean IME".
|
||||
|
||||
|
||||
Open Setting in Windows 11 and go to:
|
||||
|
||||
Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,
|
||||
|
||||
and enable "Use previous version of Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Sprach- und Regionen-Systemeinstellungen öffnen</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Öffnen</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Vorherige koreanische IME verwenden</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
|
||||
<system:String x:Key="homePage">Homepage</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Plug-in suchen</system:String>
|
||||
|
|
@ -118,6 +152,13 @@
|
|||
<system:String x:Key="currentActionKeywords">Aktuelles Action-Schlüsselwort</system:String>
|
||||
<system:String x:Key="newActionKeyword">Neues Aktions-Schlüsselwort</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Aktions-Schlüsselwörter ändern</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Erweiterte Einstellungen</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Aktiviert</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Priorität</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Homepage</system:String>
|
||||
<system:String x:Key="currentPriority">Aktuelle Priorität</system:String>
|
||||
<system:String x:Key="newPriority">Neue Priorität</system:String>
|
||||
<system:String x:Key="priority">Priorität</system:String>
|
||||
|
|
@ -129,8 +170,10 @@
|
|||
<system:String x:Key="plugin_query_version">Version</system:String>
|
||||
<system:String x:Key="plugin_query_web">Website</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Deinstallieren</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Plug-in-Einstellungen können nicht entfernt werden</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plug-ins: {0} - Plug-in-Einstellungsdateien können nicht entfernt werden, bitte entfernen Sie diese manuell</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plug-in-Store</system:String>
|
||||
|
|
@ -167,6 +210,9 @@
|
|||
<system:String x:Key="resultItemFont">Schriftart des Ergebnistitels</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Schriftart des Ergebnis-Untertitels</system:String>
|
||||
<system:String x:Key="resetCustomize">Zurücksetzen</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Individuell anpassen</system:String>
|
||||
<system:String x:Key="windowMode">Fenstermodus</system:String>
|
||||
<system:String x:Key="opacity">Opazität</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">Benutzerdefiniert</system:String>
|
||||
<system:String x:Key="Clock">Uhr</system:String>
|
||||
<system:String x:Key="Date">Datum</system:String>
|
||||
<system:String x:Key="BackdropType">Backdrop-Typ</system:String>
|
||||
<system:String x:Key="BackdropInfo">The backdrop effect is not applied in the preview.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">Keine</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">Dieses Theme unterstützt zwei Modi (hell/dunkel).</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">Dieses Theme unterstützt Unschärfe und transparenten Hintergrund.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Platzhalter zeigen</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||
<system:String x:Key="PlaceholderText">Platzhaltertext</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Festgelegte Fenstergröße</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window size is not adjustable by dragging.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Hotkey</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">Segoe Fluent-Icons verwenden</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Segoe Fluent-Icons für Abfrageergebnisse verwenden, wo unterstützt</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Taste drücken</system:String>
|
||||
<system:String x:Key="showBadges">Ergebnis-Badges zeigen</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">For supported plugins, badges are displayed to help distinguish them more easily.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP-Proxy</system:String>
|
||||
|
|
@ -294,16 +356,23 @@
|
|||
<system:String x:Key="logfolder">Ordner »Logs«</system:String>
|
||||
<system:String x:Key="clearlogfolder">Logs löschen</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">Sind Sie sicher, dass Sie alle Logs löschen wollen?</system:String>
|
||||
<system:String x:Key="cachefolder">Cache-Ordner</system:String>
|
||||
<system:String x:Key="clearcachefolder">Cache leeren</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||
<system:String x:Key="welcomewindow">Assistent</system:String>
|
||||
<system:String x:Key="userdatapath">Speicherort für Benutzerdaten</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">Benutzereinstellungen und installierte Plug-ins werden im Ordner für Benutzerdaten gespeichert. Dieser Speicherort kann variieren, je nachdem, ob sich das Programm im portablen Modus befindet oder nicht.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Ordner öffnen</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Log-Ebene</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Dateimanager auswählen</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Mehr erfahren</system:String>
|
||||
<system:String x:Key="fileManager_tips">Bitte geben Sie den Dateiort des von Ihnen verwendeten Dateimanagers an und fügen Sie bei Bedarf Argumente hinzu. Das „%d“ repräsentiert den dafür zu öffnenden Verzeichnispfad, der vom Feld Arg for Folder und für Befehle zum Öffnen bestimmter Verzeichnisse verwendet wird. Das „%f“ repräsentiert den dafür zu öffnenden Dateipfad, der vom Feld Arg for File und für Befehle zum Öffnen bestimmter Dateien verwendet wird.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">Zum Beispiel, wenn der Dateimanager einen Befehl wie „totalcmd.exe /A c:\windows“ verwendet, um das Verzeichnis c:\windows zu öffnen, lautet der Dateimanager-Pfad „totalcmd.exe“ und der Arg for Folder „/A %d“. Bestimmte Dateimanager wie QTTabBar kann nur die Angabe eines Pfades erfordern, in diesem Fall verwenden Sie „%d“ als den Dateimanager-Pfad und lassen den Rest der Felder blank.</system:String>
|
||||
<system:String x:Key="fileManager_name">Dateimanager</system:String>
|
||||
|
|
@ -311,6 +380,8 @@
|
|||
<system:String x:Key="fileManager_path">Dateimanager-Pfad</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Arg For Folder</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Webbrowser per Default</system:String>
|
||||
|
|
@ -335,9 +406,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">Das angegebene Plug-in kann nicht gefunden werden</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">Neues Aktions-Schlüsselwort darf nicht leer sein</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">Dieses neue Aktions-Schlüsselwort ist bereits einem anderen Plug-in zugewiesen, bitte wählen Sie ein anderes</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">Dieses neue Aktions-Schlüsselwort ist dasselbe wie das alte, bitte wählen Sie ein anderes</system:String>
|
||||
<system:String x:Key="success">Erfolg</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Erfolgreich abgeschlossen</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Geben Sie das Aktions-Schlüsselwort ein, das Sie verwenden möchten, um das Plug-in zu starten. Verwenden Sie *, wenn Sie keines angeben möchten, und das Plug-in wird ohne irgendwelche Aktions-Schlüsselwörter ausgelöst.</system:String>
|
||||
<system:String x:Key="failedToCopy">Failed to copy</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Geben Sie die Aktions-Schlüsselwörter ein, die Sie zum Starten des Plug-ins verwenden möchten, und trennen Sie sie durch Leerzeichen voneinander ab. Verwenden Sie *, wenn Sie keine spezifizieren möchten, und das Plug-in wird ohne jegliche Aktions-Schlüsselwörter ausgelöst.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Homepage</system:String>
|
||||
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Benutzerdefinierter Abfrage-Hotkey</system:String>
|
||||
|
|
@ -388,9 +469,17 @@ Wenn Sie bei der Eingabe eines Shortcuts ein '@'-Präfix hinzufügen, stimmt die
|
|||
<system:String x:Key="reportWindow_report_succeed">Bericht erfolgreich gesendet</system:String>
|
||||
<system:String x:Key="reportWindow_report_failed">Bericht konnte nicht gesendet werden</system:String>
|
||||
<system:String x:Key="reportWindow_flowlauncher_got_an_error">Flow Launcher hat einen Fehler</system:String>
|
||||
<system:String x:Key="reportWindow_please_open_issue">Please open new issue in</system:String>
|
||||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
<system:String x:Key="reportWindow_please_open_issue">Bitte öffnen Sie einen neuen Fall in</system:String>
|
||||
<system:String x:Key="reportWindow_upload_log">1. Logdatei hochladen: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Kopieren Sie die Ausnahmemeldung unterhalb</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Fehler</system:String>
|
||||
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Bitte warten Sie ...</system:String>
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@
|
|||
<system:String x:Key="GameMode">Game Mode</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Suspend the use of Hotkeys.</system:String>
|
||||
<system:String x:Key="PositionReset">Position Reset</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Type here to search</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
|
|
@ -57,7 +58,7 @@
|
|||
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Hide Flow Launcher when focus is lost</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Do not show new version notifications</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Remember Last Position</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Monitor with Mouse Cursor</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Monitor with Focused Window</system:String>
|
||||
|
|
@ -108,14 +109,28 @@
|
|||
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">Shadow effect is not allowed while current theme has blur effect enabled</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Delay for a while to search when typing. This reduces interface jumpiness and result load.</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Plugin default delay time after which search results appear when typing is stopped.</system:String>
|
||||
<system:String x:Key="SearchDelayTimeVeryLong">Very long</system:String>
|
||||
<system:String x:Key="SearchDelayTimeLong">Long</system:String>
|
||||
<system:String x:Key="SearchDelayTimeNormal">Normal</system:String>
|
||||
<system:String x:Key="SearchDelayTimeShort">Short</system:String>
|
||||
<system:String x:Key="SearchDelayTimeVeryShort">Very short</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Wait time before showing results after typing stops. Higher values wait longer. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.

|
||||
If you experience any problems, you may need to enable "Use previous version of Korean IME".


|
||||
Open Setting in Windows 11 and go to:

|
||||
Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,

|
||||
and enable "Use previous version of Microsoft IME".


|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Open Language and Region System Settings</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Open</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Use Previous Korean IME</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
|
||||
<system:String x:Key="homePage">Home Page</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
|
|
@ -132,8 +147,13 @@
|
|||
<system:String x:Key="currentActionKeywords">Current action keyword</system:String>
|
||||
<system:String x:Key="newActionKeyword">New action keyword</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Change Action Keywords</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin seach delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Seach Delay Time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Advanced Settings:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Enabled</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Priority</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Home Page</system:String>
|
||||
<system:String x:Key="currentPriority">Current Priority</system:String>
|
||||
<system:String x:Key="newPriority">New Priority</system:String>
|
||||
<system:String x:Key="priority">Priority</system:String>
|
||||
|
|
@ -149,7 +169,6 @@
|
|||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
<system:String x:Key="default">Default</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plugin Store</system:String>
|
||||
|
|
@ -186,6 +205,9 @@
|
|||
<system:String x:Key="resultItemFont">Result Title Font</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Result Subtitle Font</system:String>
|
||||
<system:String x:Key="resetCustomize">Reset</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Customize</system:String>
|
||||
<system:String x:Key="windowMode">Window Mode</system:String>
|
||||
<system:String x:Key="opacity">Opacity</system:String>
|
||||
|
|
@ -213,12 +235,13 @@
|
|||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
<system:String x:Key="BackdropType">Backdrop Type</system:String>
|
||||
<system:String x:Key="BackdropInfo">The backdrop effect is not applied in the preview.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">None</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two (light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||
|
|
@ -285,6 +308,9 @@
|
|||
<system:String x:Key="useGlyphUI">Use Segoe Fluent Icons</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Use Segoe Fluent Icons for query results where supported</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Press Key</system:String>
|
||||
<system:String x:Key="showBadges">Show Result Badges</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">For supported plugins, badges are displayed to help distinguish them more easily.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -325,6 +351,7 @@
|
|||
<system:String x:Key="logfolder">Log Folder</system:String>
|
||||
<system:String x:Key="clearlogfolder">Clear Logs</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">Are you sure you want to delete all logs?</system:String>
|
||||
<system:String x:Key="cachefolder">Cache Folder</system:String>
|
||||
<system:String x:Key="clearcachefolder">Clear Caches</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||
|
|
@ -335,12 +362,22 @@
|
|||
<system:String x:Key="userdatapath">User Data Location</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Open Folder</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- Release Notes Window -->
|
||||
<system:String x:Key="seeMoreReleaseNotes">See more release notes on GitHub</system:String>
|
||||
<system:String x:Key="checkNetworkConnectionTitle">Failed to fetch release notes</system:String>
|
||||
<system:String x:Key="checkNetworkConnectionSubTitle">Please check your network connection or ensure GitHub is accessible</system:String>
|
||||
<system:String x:Key="appUpdateTitle">Flow Launcher has been updated to {0}</system:String>
|
||||
<system:String x:Key="appUpdateButtonContent">Click here to view the release notes</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Select File Manager</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Learn more</system:String>
|
||||
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments as required. The "%d" represents the directory path to open for, used by the Arg for Folder field and for commands opening specific directories. The "%f" represents the file path to open for, used by the Arg for File field and for commands opening specific files.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">For example, if the file manager uses a command such as "totalcmd.exe /A c:\windows" to open the c:\windows directory, the File Manager Path will be totalcmd.exe, and the Arg For Folder will be /A "%d". Certain file managers like QTTabBar may just require a path to be supplied, in this instance use "%d" as the File Manager Path and leave the rest of the fileds blank.</system:String>
|
||||
<system:String x:Key="fileManager_name">File Manager</system:String>
|
||||
|
|
@ -348,6 +385,8 @@
|
|||
<system:String x:Key="fileManager_path">File Manager Path</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Arg For Folder</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Arg For File</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
|
||||
|
|
@ -375,13 +414,16 @@
|
|||
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
|
||||
<system:String x:Key="success">Success</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Completed successfully</system:String>
|
||||
<system:String x:Key="failedToCopy">Failed to copy</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTime_tips">Select the search delay time you like to use for the plugin. Select "{0}" if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
<system:String x:Key="currentSearchDelayTime">Current search delay time</system:String>
|
||||
<system:String x:Key="newSearchDelayTime">New search delay time</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Home Page</system:String>
|
||||
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Custom Query Hotkey</system:String>
|
||||
|
|
@ -434,6 +476,15 @@
|
|||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Error</system:String>
|
||||
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
|
||||
<system:String x:Key="browserOpenError">An error occurred while opening the URL in the browser. Please check your Default Web Browser configuration in the General section of the settings window</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Please wait...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
Click no if it's already installed, and you will be prompted to select the folder that contains the {1} executable
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Please select the {0} executable</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
Your selected {0} executable is invalid.
|
||||
{2}{2}
|
||||
Click yes if you would like select the {0} executable agian. Click no if you would like to download {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Unable to set {0} executable path, please try from Flow's settings (scroll down to the bottom).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">Fail to Init Plugins</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Plugins: {0} - fail to load and would be disabled, please contact plugin creator for help</system:String>
|
||||
|
|
@ -38,6 +43,7 @@
|
|||
<system:String x:Key="GameModeToolTip">Suspender el uso de las teclas de acceso directo.</system:String>
|
||||
<system:String x:Key="PositionReset">Position Reset</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Type here to search</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Ajustes</system:String>
|
||||
|
|
@ -50,7 +56,7 @@
|
|||
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Ocultar Flow Launcher cuando se pierde el enfoque</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">No mostrar notificaciones de nuevas versiones</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Location</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Remember Last Position</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Monitor with Mouse Cursor</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Monitor with Focused Window</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">Borrar última consulta</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Preserve Last Action Keyword</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Select Last Action Keyword</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Height</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window height is not adjustable by dragging.</system:String>
|
||||
<system:String x:Key="maxShowResults">Máximo de resultados mostrados</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">You can also quickly adjust this by using CTRL+Plus and CTRL+Minus.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorar atajos de teclado en modo pantalla completa</system:String>
|
||||
|
|
@ -102,6 +106,36 @@
|
|||
<system:String x:Key="AlwaysPreview">Always Preview</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Always open preview panel when Flow activates. Press {0} to toggle preview.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque habilitado</system:String>
|
||||
<system:String x:Key="searchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Adds a short delay while typing to reduce UI flicker and result load. Recommended if your typing speed is average.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Enter the wait time (in ms) until input is considered complete. This can only be edited if Search Delay is enabled.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Default Search Delay Time</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Wait time before showing results after typing stops. Higher values wait longer. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
The Korean input method used in Windows 11 may cause some issues in Flow Launcher.
|
||||
|
||||
If you experience any problems, you may need to enable "Use previous version of Korean IME".
|
||||
|
||||
|
||||
Open Setting in Windows 11 and go to:
|
||||
|
||||
Time & Language > Language & Region > Korean > Language Options > Keyboard - Microsoft IME > Compatibility,
|
||||
|
||||
and enable "Use previous version of Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Open Language and Region System Settings</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Opens the Korean IME setting location. Go to Korean > Language Options > Keyboard - Microsoft IME > Compatibility</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Abrir</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Use Previous Korean IME</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">You can change the Previous Korean IME settings directly from here</system:String>
|
||||
<system:String x:Key="homePage">Home Page</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">This can only be edited if plugin supports Home feature and Home Page is enabled.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
|
|
@ -118,6 +152,13 @@
|
|||
<system:String x:Key="currentActionKeywords">Palabra clave actual</system:String>
|
||||
<system:String x:Key="newActionKeyword">Nueva palabra clave</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Cambiar palabras clave</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Plugin search delay time</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Change Plugin Search Delay Time</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Advanced Settings:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Enabled</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Prioridad</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Search Delay</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Home Page</system:String>
|
||||
<system:String x:Key="currentPriority">Prioridad Actual</system:String>
|
||||
<system:String x:Key="newPriority">Nueva Prioridad</system:String>
|
||||
<system:String x:Key="priority">Prioridad</system:String>
|
||||
|
|
@ -131,6 +172,8 @@
|
|||
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fail to remove plugin settings</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins: {0} - Fail to remove plugin settings files, please remove them manually</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fail to remove plugin cache</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins: {0} - Fail to remove plugin cache files, please remove them manually</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Tienda de Plugins</system:String>
|
||||
|
|
@ -167,6 +210,9 @@
|
|||
<system:String x:Key="resultItemFont">Result Title Font</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Result Subtitle Font</system:String>
|
||||
<system:String x:Key="resetCustomize">Reset</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Reset to the recommended font and size settings.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Import Theme Size</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">If a size value intended by the theme designer is available, it will be retrieved and applied.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Customize</system:String>
|
||||
<system:String x:Key="windowMode">Modo Ventana</system:String>
|
||||
<system:String x:Key="opacity">Opacidad</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">Custom</system:String>
|
||||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two(light/dark) modes.</system:String>
|
||||
<system:String x:Key="BackdropType">Backdrop Type</system:String>
|
||||
<system:String x:Key="BackdropInfo">The backdrop effect is not applied in the preview.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Backdrop supported starting from Windows 11 build 22000 and above</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">None</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylic</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">This theme supports two (light/dark) modes.</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">This theme supports Blur Transparent Background.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Show placeholder</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Display placeholder when query is empty</system:String>
|
||||
<system:String x:Key="PlaceholderText">Placeholder text</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Change placeholder text. Input empty will use: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Fixed Window Size</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">The window size is not adjustable by dragging.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Tecla Rápida</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">Usar Iconos de Segoe Fluent</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Usar iconos de Segoe Fluent para resultados de consultas que sean soportados</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Press Key</system:String>
|
||||
<system:String x:Key="showBadges">Show Result Badges</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">For supported plugins, badges are displayed to help distinguish them more easily.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">Proxy HTTP</system:String>
|
||||
|
|
@ -294,16 +356,23 @@
|
|||
<system:String x:Key="logfolder">Carpeta de registros</system:String>
|
||||
<system:String x:Key="clearlogfolder">Clear Logs</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">Are you sure you want to delete all logs?</system:String>
|
||||
<system:String x:Key="cachefolder">Cache Folder</system:String>
|
||||
<system:String x:Key="clearcachefolder">Clear Caches</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Are you sure you want to delete all caches?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Failed to clear part of folders and files. Please see log file for more information</system:String>
|
||||
<system:String x:Key="welcomewindow">Asistente</system:String>
|
||||
<system:String x:Key="userdatapath">User Data Location</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">User settings and installed plugins are saved in the user data folder. This location may vary depending on whether it's in portable mode or not.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Open Folder</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Seleccionar Gestor de Archivos</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Learn more</system:String>
|
||||
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments as required. The "%d" represents the directory path to open for, used by the Arg for Folder field and for commands opening specific directories. The "%f" represents the file path to open for, used by the Arg for File field and for commands opening specific files.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">For example, if the file manager uses a command such as "totalcmd.exe /A c:\windows" to open the c:\windows directory, the File Manager Path will be totalcmd.exe, and the Arg For Folder will be /A "%d". Certain file managers like QTTabBar may just require a path to be supplied, in this instance use "%d" as the File Manager Path and leave the rest of the fileds blank.</system:String>
|
||||
<system:String x:Key="fileManager_name">Gestor de Archivos</system:String>
|
||||
|
|
@ -311,6 +380,8 @@
|
|||
<system:String x:Key="fileManager_path">Ruta del Gestor de Archivos</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Arg para Carpeta</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Arg para Archivo</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">The file manager '{0}' could not be located at '{1}'. Would you like to continue?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">File Manager Path Error</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Navegador Web Predeterminado</system:String>
|
||||
|
|
@ -335,9 +406,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">No se puede encontrar el plugin especificado</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">La nueva palabra clave no puede estar vacía</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">Esta palabra clave ya está asignada a otro plugin, por favor elija una diferente</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">This new Action Keyword is the same as old, please choose a different one</system:String>
|
||||
<system:String x:Key="success">Éxito</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Completado con éxito</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Introduzca la palabra clave que desea utilizar para iniciar el plugin. Utilice * si no desea especificar ninguno, y el plugin se activará sin ninguna palabra clave.</system:String>
|
||||
<system:String x:Key="failedToCopy">Failed to copy</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Enter the action keywords you like to use to start the plugin and use whitespace to divide them. Use * if you don't want to specify any, and the plugin will be triggered without any action keywords.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Search Delay Time Setting</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Input the search delay time in ms you like to use for the plugin. Input empty if you don't want to specify any, and the plugin will use default search delay time.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Home Page</system:String>
|
||||
<system:String x:Key="homeTips">Enable the plugin home page state if you like to show the plugin results when query is empty.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Tecla de Acceso Personalizada</system:String>
|
||||
|
|
@ -392,6 +473,14 @@ If you add an '@' prefix while inputting a shortcut, it matches any position in
|
|||
<system:String x:Key="reportWindow_upload_log">1. Upload log file: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copy below exception message</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">File Manager Error</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
The specified file manager could not be found. Please check the Custom File Manager setting under Settings > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Error</system:String>
|
||||
<system:String x:Key="folderOpenError">An error occurred while opening the folder. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Por favor espere...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
Haga clic en no si ya está instalado, y seleccione la carpeta que contiene el ejecutable {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Por favor, seleccione el ejecutable {0}</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
El ejecutable {0} seleccionado no es válido.
|
||||
{2}{2}
|
||||
Pulsar Sí, si desea seleccionar de nuevo el ejecutable {0}. Pulsar No, si desea descargar {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">No se puede establecer la ruta del ejecutable {0}, por favor inténtelo desde la configuración de Flow (desplácese hacia abajo).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">Fallo al iniciar los complementos</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Complemento: {0} - no se pudo cargar y se desactivará, póngase en contacto con el creador del complemento para obtener ayuda</system:String>
|
||||
|
|
@ -38,6 +43,7 @@
|
|||
<system:String x:Key="GameModeToolTip">Suspende el uso de atajos de teclado.</system:String>
|
||||
<system:String x:Key="PositionReset">Restablecer posición</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Restablece la posición de la ventana de búsqueda</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Escribir aquí para buscar</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Configuración</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">Limpiar la última consulta</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Conservar última palabra clave de acción</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Seleccionar última palabra clave de acción</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Altura de la ventana fija</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">La altura de la ventana no se puede ajustar arrastrando el ratón.</system:String>
|
||||
<system:String x:Key="maxShowResults">Número máximo de resultados mostrados</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">También puede ajustarse rápidamente usando Ctrl+Más(+) y Ctrl+Menos(-).</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignorar atajos de teclado en modo pantalla completa</system:String>
|
||||
|
|
@ -102,6 +106,36 @@
|
|||
<system:String x:Key="AlwaysPreview">Mostrar siempre vista previa</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Muestra siempre el panel de vista previa al iniciar Flow. Pulsar {0} para mostrar/ocultar la vista previa.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">El efecto de sombra no está permitido si el tema actual tiene activado el efecto de desenfoque</system:String>
|
||||
<system:String x:Key="searchDelay">Retardo de búsqueda</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Añade un breve retardo al escribir para reducir el parpadeo de la interfaz de usuario y la carga de resultados. Recomendado si la velocidad de escritura es media.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Introduzca el tiempo de espera (en ms) hasta que la entrada se considere completa. Solo se puede editar cuando el retardo de búsqueda está activado.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Tiempo de retardo de búsqueda predeterminado</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Tiempo de espera antes de mostrar los resultados después de dejar de teclear. A mayor valor, más tiempo de espera. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Información para usuario de IME coreano</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
El método de entrada coreano utilizado en Windows 11 puede causar algunos problemas en Flow Launcher.
|
||||
|
||||
Si se experimenta algún problema, es posible que se tenga que activar "Usar versión anterior del IME coreano".
|
||||
|
||||
|
||||
Abrir Configuración en Windows 11 e ir a:
|
||||
|
||||
Hora e idioma > Idioma y región > Coreano > Opciones de idioma > Teclado - Microsoft IME > Compatibilidad,
|
||||
|
||||
y activar "Usar versión anterior de Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Abrir idioma y región en configuración</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Abre la ubicación de configuración del IME coreano. Ir a Coreano > Opciones de idioma > Teclado - Microsoft IME > Compatibilidad</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Abrir</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Utilizar IME Coreano anterior</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">Se puede cambiar la configuración anterior del IME coreano directamente desde aquí</system:String>
|
||||
<system:String x:Key="homePage">Página de inicio</system:String>
|
||||
<system:String x:Key="homePageToolTip">Muestra los resultados de la página de inicio cuando el texto de la consulta está vacío.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Mostrar historial de resultados en la página de inicio</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Número máximo de resultados del historial en la página de inicio</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">Esto solo se puede editar si el complemento soporta la función de Inicio y la Página de Inicio está activada.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Buscar complemento</system:String>
|
||||
|
|
@ -118,6 +152,13 @@
|
|||
<system:String x:Key="currentActionKeywords">Palabra clave de acción actual</system:String>
|
||||
<system:String x:Key="newActionKeyword">Nueva palabra clave de acción</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Cambia la palabra clave de acción</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Tiempo de retardo de la búsqueda del complemento</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Cambia el tiempo de retardo de la búsqueda del complemento</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Configuración avanzada:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Activado</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Prioridad</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Retardo de búsqueda</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Página de inicio</system:String>
|
||||
<system:String x:Key="currentPriority">Prioridad actual</system:String>
|
||||
<system:String x:Key="newPriority">Nueva prioridad</system:String>
|
||||
<system:String x:Key="priority">Prioridad</system:String>
|
||||
|
|
@ -131,6 +172,8 @@
|
|||
<system:String x:Key="plugin_uninstall">Desinstalar</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Fallo al eliminar la configuración del complemento</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Complementos: {0} - Fallo al eliminar los archivos de configuración del complemento, por favor elimínelos manualmente</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Fallo al eliminar la caché del complemento</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Complementos: {0} - Fallo al eliminar los archivos de caché del complemento, por favor elimínelos manualmente</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Tienda complementos</system:String>
|
||||
|
|
@ -167,6 +210,9 @@
|
|||
<system:String x:Key="resultItemFont">Fuente del título del resultado</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Fuente del subtítulo del resultado</system:String>
|
||||
<system:String x:Key="resetCustomize">Restablecer</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Restablece la configuración recomendada para la fuente y el tamaño.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Importar tamaño del tema</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">Si existe un valor de tamaño del tema previsto por el diseñador, este se recuperará y aplicará.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Personaliza</system:String>
|
||||
<system:String x:Key="windowMode">Modo Ventana</system:String>
|
||||
<system:String x:Key="opacity">Opacidad</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">Personalizada</system:String>
|
||||
<system:String x:Key="Clock">Reloj</system:String>
|
||||
<system:String x:Key="Date">Fecha</system:String>
|
||||
<system:String x:Key="BackdropType">Tipo de telón de fondo</system:String>
|
||||
<system:String x:Key="BackdropInfo">El efecto de telón de fondo no se aplica en la vista previa.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Telón de fondo compatible a partir de Windows 11 build 22000 y superiores</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">Ninguno</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrílico</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">Este tema soporta dos modos (claro/oscuro).</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">Este tema soporta fondo transparente desenfocado.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Mostrar marcador de posición</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Mostrar marcador de posición cuando la consulta esté vacía</system:String>
|
||||
<system:String x:Key="PlaceholderText">Texto del marcador de posición</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Cambiar el texto del marcador de posición. La entrada vacía utilizará: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Tamaño fijo de la ventana</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">El tamaño de la ventana no se puede ajustar mediante arrastre.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Atajo de teclado</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">Iconos Segoe Fluent</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Utiliza iconos Segoe Fluent para los resultados de la consulta cuando sean compatibles</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Pulsar Tecla</system:String>
|
||||
<system:String x:Key="showBadges">Mostrar distintivos en resultados</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">Para los complementos compatibles, se muestran distintivos que ayudan a distinguirlos más fácilmente.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Mostrar distintivos en resultados solo para consulta global</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">Proxy HTTP</system:String>
|
||||
|
|
@ -294,16 +356,23 @@
|
|||
<system:String x:Key="logfolder">Carpeta de registros</system:String>
|
||||
<system:String x:Key="clearlogfolder">Eliminar registros</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">¿Está seguro de que desea eliminar todos los registros?</system:String>
|
||||
<system:String x:Key="cachefolder">Carpeta del caché</system:String>
|
||||
<system:String x:Key="clearcachefolder">Limpiar cachés</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">¿Está seguro de que desea eliminar todos los cachés?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">No se pudo eliminar parte de las carpetas y archivos. Por favor, consulte el archivo de registro para más información</system:String>
|
||||
<system:String x:Key="welcomewindow">Asistente</system:String>
|
||||
<system:String x:Key="userdatapath">Ubicación de datos del usuario</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">La configuración del usuario y los complementos instalados se guardan en la carpeta de datos del usuario. Esta ubicación puede variar dependiendo de si está en modo portable o no.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Abrir carpeta</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">Nivel de registro</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Depurar</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Información</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Configuración de fuente de la ventana</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Seleccionar administrador de archivos</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">Más información</system:String>
|
||||
<system:String x:Key="fileManager_tips">Especifique la ubicación del archivo del administrador de archivos que está utilizando y añada los argumentos necesarios. El argumento "%d" representa la ruta del directorio a abrir, utilizada por el campo Argumentos de la carpeta y por comandos que abren directorios específicos. El "%f" representa la ruta del archivo a abrir, utilizada por el campo Argumentos del archivo y por comandos que abren archivos específicos.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">Por ejemplo, si el administrador de archivos utiliza un comando como "totalcmd.exe /A c:\windows" para abrir el directorio c:\windows, la ruta del administrador de archivos será totalcmd.exe, y los Argumentos de la carpeta serán /A "%d". Ciertos administradores de archivos como QTTabBar pueden requerir solo la ruta, en este caso utilice "%d" como la ruta del administrador de archivos y deje el resto de los campos en blanco.</system:String>
|
||||
<system:String x:Key="fileManager_name">Administrador de archivos</system:String>
|
||||
|
|
@ -311,6 +380,8 @@
|
|||
<system:String x:Key="fileManager_path">Ruta del administrador de archivos</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Argumentos de la carpeta</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Argumentos del archivo</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">El administrador de archivos '{0}' no pudo ser localizado en '{1}'. ¿Desea continuar?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">Error de ruta del administrador de archivos</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Navegador web predeterminado</system:String>
|
||||
|
|
@ -335,9 +406,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">No se puede encontrar el complemento especificado</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">La nueva palabra clave de acción no puede estar vacía</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">Esta nueva palabra clave de acción ya está asignada a otro complemento, por favor elija una diferente</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">Esta nueva palabra clave de acción es la misma que la anterior, por favor elija una diferente</system:String>
|
||||
<system:String x:Key="success">Correcto</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Finalizado correctamente</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Introduzca la palabra clave que desea utilizar para iniciar el complemento. Utilice * si no desea especificar ninguna, y el complemento se activará sin ninguna palabra clave.</system:String>
|
||||
<system:String x:Key="failedToCopy">No se pudo copiar</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Introduzca las palabras clave de acción que desea utilizar para iniciar el complemento y utilice espacios en blanco para separarlas. Utilice * si no desea especificar ninguna, para que el complemento se inicie sin ninguna palabra clave de acción.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Ajuste del tiempo de retardo de búsqueda</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Introducir el tiempo de retardo de búsqueda en ms que se desea utilizar para el complemento. Introducir un espacio vacío si no desea especificar ninguno, y el complemento utilizará el tiempo de retardo de búsqueda predeterminado.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Página de inicio</system:String>
|
||||
<system:String x:Key="homeTips">Activar el estado de la página de inicio del complemento si se desea mostrar los resultados del complemento cuando la consulta está vacía.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Atajo de teclado de consulta personalizada</system:String>
|
||||
|
|
@ -392,6 +473,14 @@ Si añade un prefijo "@" al introducir un acceso directo, éste coinci
|
|||
<system:String x:Key="reportWindow_upload_log">1. Subir archivo de registro: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copiar el siguiente mensaje de excepción</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">Error del administrador de archivos</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
No se ha encontrado el administrador de archivos especificado. Compruebe la configuración del Administrador de archivos personalizado en Configuración > General.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Error</system:String>
|
||||
<system:String x:Key="folderOpenError">Se ha producido un error al abrir la carpeta. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Por favor espere...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
Cliquez sur non s'il est déjà installé, et vous serez invité à sélectionner le dossier qui contient l'exécutable {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">Veuillez sélectionner l'exécutable {0}</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
L'exécutable {0} que vous avez sélectionné est invalide.
|
||||
{2}{2}
|
||||
Cliquez sur oui si vous souhaitez sélectionner l'exécutable {0} à nouveau. Cliquez sur non si vous souhaitez télécharger {1}.
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">Impossible de définir {0} comme chemin d'accès vers l'exécutable. Veuillez essayer à partir des paramètres de Flow (défiler vers le bas).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">Échec de l'initialisation des plugins</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">Plugins : {0} - n'ont pas pu être chargés et doivent être désactivés, veuillez contacter le créateur du plugin pour obtenir de l'aide</system:String>
|
||||
|
|
@ -37,7 +42,8 @@
|
|||
<system:String x:Key="GameMode">Mode jeu</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Suspend l'utilisation des raccourcis claviers.</system:String>
|
||||
<system:String x:Key="PositionReset">Réinitialiser la position</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Rétablir la position de la fenêtre de recherche</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Réinitialiser la position de la fenêtre de recherche</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">Tapez ici pour rechercher</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Paramètres</system:String>
|
||||
|
|
@ -50,7 +56,7 @@
|
|||
<system:String x:Key="setAutoStartFailed">Erreur lors de la configuration du lancement au démarrage</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Cacher Flow Launcher lors de la perte de focus</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Ne pas afficher le message de mise à jour pour les nouvelles versions</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Position de la fenêtre de recherche</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Emplacement de la fenêtre de recherche</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">Se souvenir de la dernière position</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Surveiller avec le curseur de la souris</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Surveiller avec la fenêtre ciblée</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">Ne pas afficher la dernière recherche</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">Conserver le mot clé de la dernière action</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">Sélectionnez le mot clé de la dernière action</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Hauteur de fenêtre fixe</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">La hauteur de la fenêtre n'est pas réglable par glissement.</system:String>
|
||||
<system:String x:Key="maxShowResults">Résultats maximums à afficher</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">Vous pouvez également ajuster ce paramètre en utilisant CTRL+Plus ou CTRL+Moins.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">Ignore les raccourcis lorsqu'une application est en plein écran</system:String>
|
||||
|
|
@ -102,6 +106,36 @@
|
|||
<system:String x:Key="AlwaysPreview">Toujours prévisualiser</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">Toujours ouvrir le panneau d'aperçu lorsque Flow s'active. Appuyez sur {0} pour activer/désactiver l'aperçu.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">L'effet d'ombre n'est pas autorisé lorsque le thème actuel à un effet de flou activé</system:String>
|
||||
<system:String x:Key="searchDelay">Délai de recherche</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">Ajoute un court délai pendant la frappe pour réduire le scintillement de l'interface utilisateur et le chargement des résultats. Recommandé si votre vitesse de frappe est moyenne.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">Entrez le temps d'attente (en ms) jusqu'à ce que l'entrée soit considérée comme terminée. Cela ne peut être modifié que si le délai de recherche est activé.</system:String>
|
||||
<system:String x:Key="searchDelayTime">Délai de recherche par défaut</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">Délai d'attente avant l'affichage des résultats après l'arrêt de la saisie. Les valeurs élevées permettent d'attendre plus longtemps. (ms)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information pour les utilisateurs coréens IME</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
La méthode de saisie coréenne utilisée dans Windows 11 peut causer des problèmes dans Flow Launcher.
|
||||
|
||||
Si vous rencontrez des problèmes, il se peut que vous deviez activer l'option "Utiliser la version précédente de l'IME coréen".
|
||||
|
||||
|
||||
Ouvrez les Paramètres dans Windows 11 et allez dans :
|
||||
|
||||
Heure et langue > Langue et région > Coréen > Options linguistiques > Claviers - Microsoft IME > Compatibilité,
|
||||
|
||||
et activez l'option "Utiliser la version précédente de Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">Ouvrir les paramètres du système de langue et de région</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">Ouvre l'emplacement de réglage IME coréen. Allez dans coréen > Options linguistiques > Claviers - Microsoft IME > Compatibilité</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">Ouvrir</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">Utilisez l'IME coréenne précédente</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">Vous pouvez modifier les paramètres de l'IME coréen précédent directement à partir d'ici</system:String>
|
||||
<system:String x:Key="homePage">Page d'accueil</system:String>
|
||||
<system:String x:Key="homePageToolTip">Afficher les résultats de la page d'accueil lorsque le texte de la requête est vide.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Afficher les résultats de l'historique sur la page d'accueil</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum de résultats de l'historique affichés sur la page d'accueil</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">Ceci ne peut être édité que si le plugin prend en charge la fonction Accueil et que la page d'accueil est activée.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Rechercher des plugins</system:String>
|
||||
|
|
@ -118,6 +152,13 @@
|
|||
<system:String x:Key="currentActionKeywords">Mot-clé d'action actuel</system:String>
|
||||
<system:String x:Key="newActionKeyword">Nouveau mot-clé d'action</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Changer les mots-clés d'action</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">Délai de recherche du plugin</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">Modifier le délai de recherche du plugin</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">Paramètres avancés :</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">Activé</system:String>
|
||||
<system:String x:Key="DisplayModePriority">Priorité</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">Délai de recherche</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">Page d'accueil</system:String>
|
||||
<system:String x:Key="currentPriority">Priorité actuelle</system:String>
|
||||
<system:String x:Key="newPriority">Nouvelle priorité</system:String>
|
||||
<system:String x:Key="priority">Priorité</system:String>
|
||||
|
|
@ -131,6 +172,8 @@
|
|||
<system:String x:Key="plugin_uninstall">Désinstaller</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">Échec de la suppression des paramètres du plugin</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">Plugins : {0} - Échec de la suppression des fichiers de configuration des plugins, veuillez les supprimer manuellement</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">Échec de la suppression du cache du plugin</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">Plugins : {0} - Échec de la suppression des fichiers cache des plugins, veuillez les supprimer manuellement</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Magasin des Plugins</system:String>
|
||||
|
|
@ -167,6 +210,9 @@
|
|||
<system:String x:Key="resultItemFont">Police du titre du résultat</system:String>
|
||||
<system:String x:Key="resultSubItemFont">Police des sous-titres du résultat</system:String>
|
||||
<system:String x:Key="resetCustomize">Réinitialiser</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">Rétablir les paramètres de police et de taille recommandés.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">Importer la taille du thème</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">Si une valeur de taille prévue par le concepteur du thème est disponible, elle sera récupérée et appliquée.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">Personnaliser</system:String>
|
||||
<system:String x:Key="windowMode">Mode fenêtré</system:String>
|
||||
<system:String x:Key="opacity">Opacité</system:String>
|
||||
|
|
@ -193,8 +239,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">Personnalisé</system:String>
|
||||
<system:String x:Key="Clock">Heure</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
<system:String x:Key="BackdropType">Type d'arrière-plan</system:String>
|
||||
<system:String x:Key="BackdropInfo">L'effet de fond n'est pas appliqué dans l'aperçu.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">Arrière-plan pris en charge à partir de Windows 11 version 22000 et plus</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">Aucun</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">Acrylique</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">Mica</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">Ce thème prend en charge deux modes (clair/sombre).</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">Ce thème prend en charge l'arrière-plan flou et transparent.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">Afficher l'espace réservé</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">Afficher un espace réservé lorsque la requête est vide</system:String>
|
||||
<system:String x:Key="PlaceholderText">Texte de l'espace réservé</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">Modifier le texte de l'espace réservé. Les entrées vides utiliseront : {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">Taille de la fenêtre fixe</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">La taille de la fenêtre n'est pas réglable par glissement.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Raccourcis</system:String>
|
||||
|
|
@ -254,6 +313,9 @@
|
|||
<system:String x:Key="useGlyphUI">Utiliser les icônes Segoe Fluent</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Utiliser les icônes Segoe Fluent pour les résultats de requête lorsque pris en charge</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Appuyez sur une touche</system:String>
|
||||
<system:String x:Key="showBadges">Afficher les badges de résultats</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">Pour les plugins pris en charge, des badges sont affichés afin de les distinguer plus facilement.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Afficher les badges de résultats pour la requête globale uniquement</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">Proxy HTTP</system:String>
|
||||
|
|
@ -293,16 +355,23 @@
|
|||
<system:String x:Key="logfolder">Répertoire des journaux</system:String>
|
||||
<system:String x:Key="clearlogfolder">Effacer le journal</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">Êtes-vous sûr de vouloir supprimer tous les journaux ?</system:String>
|
||||
<system:String x:Key="cachefolder">Dossier de cache</system:String>
|
||||
<system:String x:Key="clearcachefolder">Vider les caches</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">Êtes-vous sûr de vouloir supprimer tous les caches ?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">Échec de l'effacement d'une partie des dossiers et des fichiers. Veuillez consulter le fichier journal pour plus d'informations</system:String>
|
||||
<system:String x:Key="welcomewindow">Assistant</system:String>
|
||||
<system:String x:Key="userdatapath">Emplacement des données utilisateur</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">Les paramètres utilisateur et les plugins installés sont enregistrés dans le dossier des données utilisateur. Cet emplacement peut varier selon que vous soyez en mode portable ou non.</system:String>
|
||||
<system:String x:Key="userdatapathButton">Ouvrir le dossier</system:String>
|
||||
<system:String x:Key="advanced">Avancé</system:String>
|
||||
<system:String x:Key="logLevel">Niveau de journalisation</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Débogage</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Réglage de la police de la fenêtre</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Sélectionner le gestionnaire de fichiers</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">En savoir plus</system:String>
|
||||
<system:String x:Key="fileManager_tips">Veuillez spécifier l'emplacement du fichier de l'explorateur de fichiers que vous utilisez et ajouter des arguments si nécessaire. Le "%d" représente le chemin du répertoire à ouvrir, utilisé par le champ Arg for Folder et pour les commandes ouvrant des répertoires spécifiques. Le "%f" représente le chemin du fichier à ouvrir, utilisé par le champ Arg for File et pour les commandes ouvrant des fichiers spécifiques.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">Par exemple, si l'explorateur de fichiers utilise une commande telle que "totalcmd.exe /A c:\windows" pour ouvrir le répertoire c:\windows, le chemin de l'explorateur de fichiers sera totalcmd.exe et l'argument Arg For Folder sera /A "%d"". Certains explorateurs de fichiers comme QTTabBar peuvent simplement nécessiter qu'un chemin soit fourni, dans ce cas, utilisez "%d" comme chemin de l'explorateur de fichiers et laissez le reste des fichiers vides.</system:String>
|
||||
<system:String x:Key="fileManager_name">Gestionnaire de fichiers</system:String>
|
||||
|
|
@ -310,6 +379,8 @@
|
|||
<system:String x:Key="fileManager_path">Chemin du gestionnaire de fichiers</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">Arguments pour le répertoire</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">Arguments pour le fichier</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">Le gestionnaire de fichiers '{0}' n'a pas pu être situé à '{1}'. Souhaitez-vous continuer ?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">Erreur de chemin du gestionnaire de fichiers</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Navigateur web par défaut</system:String>
|
||||
|
|
@ -334,9 +405,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">Impossible de trouver le module spécifi</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">Le nouveau mot-clé d'action doit être spécifi</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">Le nouveau mot-clé d'action a été assigné à un autre module, veuillez en choisir un autre</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">Ce nouveau mot-clé d'action est identique à l'ancien, veuillez en choisir un autre</system:String>
|
||||
<system:String x:Key="success">Ajout</system:String>
|
||||
<system:String x:Key="completedSuccessfully">Terminé avec succès</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Saisissez * si vous ne souhaitez pas utiliser de mot-clé spécifique</system:String>
|
||||
<system:String x:Key="failedToCopy">Échec de la copie</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">Saisissez les mots-clés d'action que vous souhaitez utiliser pour lancer le plugin et séparez-les par des espaces. Utilisez * si vous ne voulez en spécifier aucun, et le plugin sera déclenché sans aucun mot-clé d'action.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">Réglage du délai de recherche</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">Entrez le délai de recherche en ms que vous souhaitez utiliser pour le plugin. Laissez la case vide et le plugin utilisera le délai de recherche par défaut.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">Page d'accueil</system:String>
|
||||
<system:String x:Key="homeTips">Activez l'état de la page d'accueil du plugin si vous souhaitez afficher les résultats du plugin lorsque la requête est vide.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Requêtes personnalisées</system:String>
|
||||
|
|
@ -391,6 +472,14 @@ Si vous ajoutez un préfixe "@" lors de la saisie d'un raccourci, celu
|
|||
<system:String x:Key="reportWindow_upload_log">1. Télécharger le fichier journal : {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. Copiez le message d’exception ci-dessous</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">Erreur du gestionnaire de fichiers</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
Le gestionnaire de fichiers spécifié n'a pas été trouvé. Veuillez vérifier le paramètre Gestionnaire de fichiers personnalisé dans Paramètres > Général.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">Erreur</system:String>
|
||||
<system:String x:Key="folderOpenError">Une erreur s'est produite lors de l'ouverture du dossier. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Veuillez patienter...</system:String>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
אם זה כבר מותקן, לחץ על 'לא' ותתבקש לבחור את התיקיה המכילה את קובץ ההפעלה {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginChooseRuntimeExecutable">אנא בחר את קובץ ההפעלה {0}</system:String>
|
||||
<system:String x:Key="runtimeExecutableInvalidChooseDownload">
|
||||
קובץ ההפעלה {0} שבחרת אינו חוקי.
|
||||
{2}{2}
|
||||
לחץ על כן אם ברצונך, בחר את {0} ההפעלה הקודמת. לחץ על לא אם ברצונך להוריד את {1}
|
||||
</system:String>
|
||||
<system:String x:Key="runtimePluginUnableToSetExecutablePath">לא ניתן להגדיר נתיב הפעלה {0}, אנא נסה שוב בהגדרות Flow (גלול עד למטה).</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsTitle">נכשל בהפעלת תוספים</system:String>
|
||||
<system:String x:Key="failedToInitializePluginsMessage">תוספים: {0} - נכשלו בטעינה ויושבתו, אנא צור קשר עם יוצרי התוספים לקבלת עזרה</system:String>
|
||||
|
|
@ -38,6 +43,7 @@
|
|||
<system:String x:Key="GameModeToolTip">השהה את השימוש במקשי קיצור.</system:String>
|
||||
<system:String x:Key="PositionReset">איפוס מיקום</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">אפס את מיקום חלון החיפוש</system:String>
|
||||
<system:String x:Key="queryTextBoxPlaceholder">הקלד כאן כדי לחפש</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">הגדרות</system:String>
|
||||
|
|
@ -50,7 +56,7 @@
|
|||
<system:String x:Key="setAutoStartFailed">שגיאה בהגדרת ההפעלה בעת הפעלת windows</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">הסתר את Flow Launcher כאשר הוא אינו החלון הפעיל</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">אל תציג התראות על גרסה חדשה</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">מיקום חלון החיפוש</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">מיקום חלון חיפוש</system:String>
|
||||
<system:String x:Key="SearchWindowScreenRememberLastLaunchLocation">זכור את המיקום האחרון</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCursor">Monitor with Mouse Cursor</system:String>
|
||||
<system:String x:Key="SearchWindowScreenFocus">Monitor with Focused Window</system:String>
|
||||
|
|
@ -61,7 +67,7 @@
|
|||
<system:String x:Key="SearchWindowAlignCenterTop">מרכז עליון</system:String>
|
||||
<system:String x:Key="SearchWindowAlignLeftTop">שמאל עליון</system:String>
|
||||
<system:String x:Key="SearchWindowAlignRightTop">ימין עליון</system:String>
|
||||
<system:String x:Key="SearchWindowAlignCustom">Custom Position</system:String>
|
||||
<system:String x:Key="SearchWindowAlignCustom">מיקום מותאם אישית</system:String>
|
||||
<system:String x:Key="language">שפה</system:String>
|
||||
<system:String x:Key="lastQueryMode">סגנון שאילתה אחרונה</system:String>
|
||||
<system:String x:Key="lastQueryModeToolTip">הצג/הסתר תוצאות קודמות כאשר Flow Launcher מופעל מחדש.</system:String>
|
||||
|
|
@ -70,8 +76,6 @@
|
|||
<system:String x:Key="LastQueryEmpty">נקה שאילתא אחרונה</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordPreserved">שמור מילת מפתח לפעולה האחרונה</system:String>
|
||||
<system:String x:Key="LastQueryActionKeywordSelected">בחר מילת מפתח לפעולה האחרונה</system:String>
|
||||
<system:String x:Key="KeepMaxResults">גובה חלון קבוע</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">גובה החלון אינו ניתן להתאמה באמצעות גרירה.</system:String>
|
||||
<system:String x:Key="maxShowResults">כמות תוצאות מרבית</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">ניתן גם להתאים במהירות באמצעות CTRL+פלוס ו-CTRL+מינוס.</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreen">התעלם מקיצורי מקשים במצב מסך מלא</system:String>
|
||||
|
|
@ -97,17 +101,46 @@
|
|||
<system:String x:Key="SearchPrecisionNone">ללא</system:String>
|
||||
<system:String x:Key="SearchPrecisionLow">נמוך</system:String>
|
||||
<system:String x:Key="SearchPrecisionRegular">Regular</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Search with Pinyin</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">Allows using Pinyin to search. Pinyin is the standard system of romanized spelling for translating Chinese.</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">חפש באמצעות Pinyin</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">מאפשר חיפוש באמצעות Pinyin, מערכת הכתיבה הסטנדרטית לתרגום סינית.</system:String>
|
||||
<system:String x:Key="AlwaysPreview">הצג תמיד תצוגה מקדימה</system:String>
|
||||
<system:String x:Key="AlwaysPreviewToolTip">פתח תמיד את לוח התצוגה המקדימה כאשר Flow מופעל. הקש על {0} כדי להחליף את התצוגה המקדימה.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">לא ניתן להחיל אפקט צל כאשר העיצוב הנוכחי מוגדר לאפקט טשטוש</system:String>
|
||||
<system:String x:Key="searchDelay">השהיית חיפוש</system:String>
|
||||
<system:String x:Key="searchDelayToolTip">מוסיף עיכוב קצר בזמן ההקלדה כדי להפחית קפיצות בממשק המשתמש ועומס בתוצאות. מומלץ אם מהירות ההקלדה שלך ממוצעת.</system:String>
|
||||
<system:String x:Key="searchDelayNumberBoxToolTip">הזן את זמן ההמתנה (בשניות) עד שהקלט נחשב כמושלם. ניתן לערוך זאת רק אם השהיית חיפוש מופעלת.</system:String>
|
||||
<system:String x:Key="searchDelayTime">זמן עיכוב חיפוש ברירת מחדל</system:String>
|
||||
<system:String x:Key="searchDelayTimeToolTip">זמן המתנה להצגת התוצאות לאחר שתפסיק להקליד. ערכים גבוהים יותר מייצגים המתנה רבה יותר. (שניות)</system:String>
|
||||
<system:String x:Key="KoreanImeTitle">Information for Korean IME user</system:String>
|
||||
<system:String x:Key="KoreanImeGuide">
|
||||
שיטת הקלט הקוריאנית שמשמשת ב־Windows 11 עלולה לגרום לבעיות מסוימות ב־Flow Launcher.
|
||||
|
||||
אם אתה נתקל בבעיות, ייתכן שתצטרך להפעיל את האפשרות "השתמש בגרסה הקודמת של IME הקוריאני".
|
||||
|
||||
פתח את ההגדרות ב־Windows 11 וגש אל:
|
||||
|
||||
זמן ושפה > שפה ואזור > קוריאנית > אפשרויות שפה > מקלדת - Microsoft IME > תאימות,
|
||||
|
||||
והפעל את האפשרות "השתמש בגרסה הקודמת של Microsoft IME".
|
||||
|
||||
|
||||
</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLink">פתח את הגדרות מערכת שפה ואזור</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkToolTip">פותח את מיקום הגדרות ה־IME הקוריאני. עבור אל קוריאנית > אפשרויות שפה > מקלדת - Microsoft IME > תאימות</system:String>
|
||||
<system:String x:Key="KoreanImeOpenLinkButton">פתח</system:String>
|
||||
<system:String x:Key="KoreanImeRegistry">השתמש ב־IME הקוריאני הקודם</system:String>
|
||||
<system:String x:Key="KoreanImeRegistryTooltip">באפשרותך לשנות את הגדרות ה־IME הקוריאני הקודם ישירות מכאן</system:String>
|
||||
<system:String x:Key="homePage">דף הבית</system:String>
|
||||
<system:String x:Key="homePageToolTip">Show home page results when query text is empty.</system:String>
|
||||
<system:String x:Key="historyResultsForHomePage">Show History Results in Home Page</system:String>
|
||||
<system:String x:Key="historyResultsCountForHomePage">Maximum History Results Shown in Home Page</system:String>
|
||||
<system:String x:Key="homeToggleBoxToolTip">ניתן לערוך זאת רק אם התוסף תומך בתכונת הבית ודף הבית מופעל.</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">חפש תוסף</system:String>
|
||||
<system:String x:Key="searchpluginToolTip">Ctrl+F לחיפוש תוסף</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Title">לא נמצאו תוצאות</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">Please try a different search.</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">אנא נסה חיפוש אחר.</system:String>
|
||||
<system:String x:Key="plugin">תוסף</system:String>
|
||||
<system:String x:Key="plugins">תוספים</system:String>
|
||||
<system:String x:Key="browserMorePlugins">מצא תוספים נוספים</system:String>
|
||||
|
|
@ -118,6 +151,13 @@
|
|||
<system:String x:Key="currentActionKeywords">מילת מפתח נוכחית לפעולה</system:String>
|
||||
<system:String x:Key="newActionKeyword">מילת מפתח חדשה לפעולה</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">שנה מילות מפתח לפעולה</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTime">זמן השהייה של חיפוש תוסף</system:String>
|
||||
<system:String x:Key="pluginSearchDelayTimeTooltip">שנה את זמן השהיית חיפוש של תוסף</system:String>
|
||||
<system:String x:Key="FilterComboboxLabel">הגדרות מתקדמות:</system:String>
|
||||
<system:String x:Key="DisplayModeOnOff">מופעל</system:String>
|
||||
<system:String x:Key="DisplayModePriority">עדיפות</system:String>
|
||||
<system:String x:Key="DisplayModeSearchDelay">עיכוב חיפוש</system:String>
|
||||
<system:String x:Key="DisplayModeHomeOnOff">דף הבית</system:String>
|
||||
<system:String x:Key="currentPriority">עדיפות נוכחית</system:String>
|
||||
<system:String x:Key="newPriority">עדיפות חדשה</system:String>
|
||||
<system:String x:Key="priority">עדיפות</system:String>
|
||||
|
|
@ -131,6 +171,8 @@
|
|||
<system:String x:Key="plugin_uninstall">הסר התקנה</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsTitle">נכשל בהסרת הגדרות התוסף</system:String>
|
||||
<system:String x:Key="failedToRemovePluginSettingsMessage">תוספים: {0} - נכשל בהסרת קבצי הגדרות התוסף, יש להסירם ידנית</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheTitle">נכשל בהסרת מטמון התוסף</system:String>
|
||||
<system:String x:Key="failedToRemovePluginCacheMessage">תוספים: {0} - נכשל בהסרת קובצי מטמון התוסף, אנא הסר אותם ידנית</system:String>
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">חנות תוספים</system:String>
|
||||
|
|
@ -156,7 +198,7 @@
|
|||
<system:String x:Key="SampleTitleExplorer">סייר</system:String>
|
||||
<system:String x:Key="SampleSubTitleExplorer">חפש קבצים, תיקיות ובתוכן הקבצים</system:String>
|
||||
<system:String x:Key="SampleTitleWebSearch">חיפוש באינטרנט</system:String>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">Search the web with different search engine support</system:String>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">חפש באינטרנט עם תמיכה במנועי חיפוש שונים</system:String>
|
||||
<system:String x:Key="SampleTitleProgram">תוכנה</system:String>
|
||||
<system:String x:Key="SampleSubTitleProgram">הפעל תוכנות כמנהל או כמשתמש אחר</system:String>
|
||||
<system:String x:Key="SampleTitleProcessKiller">ProcessKiller</system:String>
|
||||
|
|
@ -167,6 +209,9 @@
|
|||
<system:String x:Key="resultItemFont">גופן הכותרת לתוצאה</system:String>
|
||||
<system:String x:Key="resultSubItemFont">גופן כותרת המשנה לתוצאה</system:String>
|
||||
<system:String x:Key="resetCustomize">אפס</system:String>
|
||||
<system:String x:Key="resetCustomizeToolTip">אפס להגדרות הגופן והגודל המומלצות.</system:String>
|
||||
<system:String x:Key="ImportThemeSize">ייבוא גודל ערכת נושא</system:String>
|
||||
<system:String x:Key="ImportThemeSizeToolTip">אם ערך הגודל שתוכנן על ידי מעצב ערכת הנושא זמין, הוא יאוחזר ויוחל.</system:String>
|
||||
<system:String x:Key="CustomizeToolTip">התאם אישית</system:String>
|
||||
<system:String x:Key="windowMode">מצב חלון</system:String>
|
||||
<system:String x:Key="opacity">שקיפות</system:String>
|
||||
|
|
@ -193,8 +238,21 @@
|
|||
<system:String x:Key="AnimationSpeedCustom">מותאם אישית</system:String>
|
||||
<system:String x:Key="Clock">שעון</system:String>
|
||||
<system:String x:Key="Date">תאריך</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">ערכת נושא זאת תומך בשני מצבים (בהיר/כהה).</system:String>
|
||||
<system:String x:Key="BackdropType">סוג רקע</system:String>
|
||||
<system:String x:Key="BackdropInfo">אפקט הרקע אינו מוחל בתצוגה המקדימה.</system:String>
|
||||
<system:String x:Key="BackdropTypeDisabledToolTip">התמיכה ב-Backdrop קיימת החל מ-Windows 11 build 22000 ומעלה</system:String>
|
||||
<system:String x:Key="BackdropTypesNone">ללא</system:String>
|
||||
<system:String x:Key="BackdropTypesAcrylic">אקריליק</system:String>
|
||||
<system:String x:Key="BackdropTypesMica">מיקה</system:String>
|
||||
<system:String x:Key="BackdropTypesMicaAlt">Mica Alt</system:String>
|
||||
<system:String x:Key="TypeIsDarkToolTip">ערכת נושא זאת תומכת בשני מצבים (בהיר/כהה).</system:String>
|
||||
<system:String x:Key="TypeHasBlurToolTip">ערכת נושא זו תומכת בטשטוש רקע שקוף.</system:String>
|
||||
<system:String x:Key="ShowPlaceholder">הצג מציין מיקום</system:String>
|
||||
<system:String x:Key="ShowPlaceholderTip">הצג מציין מיקום כאשר השאילתה ריקה</system:String>
|
||||
<system:String x:Key="PlaceholderText">טקסט מציין מיקום</system:String>
|
||||
<system:String x:Key="PlaceholderTextTip">שנה את טקסט מציין המיקום. אם הקלט ריק, ייעשה שימוש ב: {0}</system:String>
|
||||
<system:String x:Key="KeepMaxResults">גודל חלון קבוע</system:String>
|
||||
<system:String x:Key="KeepMaxResultsToolTip">לא ניתן להתאים את גודל החלון באמצעות גרירה.</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">מקש קיצור</system:String>
|
||||
|
|
@ -254,6 +312,9 @@
|
|||
<system:String x:Key="useGlyphUI">השתמש ב-Segoe Fluent Icons</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">השתמש ב-Segoe Fluent Icons לתוצאות חיפוש כאשר נתמך</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">הקש על מקש</system:String>
|
||||
<system:String x:Key="showBadges">הצג תגי תוצאות</system:String>
|
||||
<system:String x:Key="showBadgesToolTip">עבור תוספים נתמכים, מוצגים תגים כדי לעזור להבחין ביניהם ביתר קלות.</system:String>
|
||||
<system:String x:Key="showBadgesGlobalOnly">Show Result Badges for Global Query Only</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -289,21 +350,28 @@
|
|||
</system:String>
|
||||
<system:String x:Key="releaseNotes">הערות שחרור</system:String>
|
||||
<system:String x:Key="documentation">טיפים לשימוש</system:String>
|
||||
<system:String x:Key="devtool">DevTools</system:String>
|
||||
<system:String x:Key="devtool">כלי פיתוח</system:String>
|
||||
<system:String x:Key="settingfolder">תיקיית ההגדרות</system:String>
|
||||
<system:String x:Key="logfolder">תיקיית יומני רישום</system:String>
|
||||
<system:String x:Key="clearlogfolder">נקה יומני רישום</system:String>
|
||||
<system:String x:Key="clearlogfolderMessage">האם אתה בטוח שברצונך למחוק את כל היומנים?</system:String>
|
||||
<system:String x:Key="cachefolder">תיקיית מטמון</system:String>
|
||||
<system:String x:Key="clearcachefolder">נקה נתוני מטמון</system:String>
|
||||
<system:String x:Key="clearcachefolderMessage">האם אתה בטוח שברצונך למחוק את כל הנתונים שבמטמון?</system:String>
|
||||
<system:String x:Key="clearfolderfailMessage">נכשל ניקוי חלק מהתיקיות והקבצים. עיין בלוג לקבלת מידע נוסף</system:String>
|
||||
<system:String x:Key="welcomewindow">אשף</system:String>
|
||||
<system:String x:Key="userdatapath">מיקום נתוני משתמש</system:String>
|
||||
<system:String x:Key="userdatapathToolTip">הגדרות המשתמש והתוספים המותקנים נשמרים בתיקיית נתוני המשתמש. מיקום זה עשוי להשתנות אם התוכנה במצב נייד.</system:String>
|
||||
<system:String x:Key="userdatapathButton">פתח תיקיה</system:String>
|
||||
<system:String x:Key="logLevel">Log Level</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">Debug</system:String>
|
||||
<system:String x:Key="LogLevelINFO">Info</system:String>
|
||||
<system:String x:Key="advanced">Advanced</system:String>
|
||||
<system:String x:Key="logLevel">רמת יומן</system:String>
|
||||
<system:String x:Key="LogLevelDEBUG">ניפוי שגיאות</system:String>
|
||||
<system:String x:Key="LogLevelINFO">מידע</system:String>
|
||||
<system:String x:Key="settingWindowFontTitle">Setting Window Font</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">בחר מנהל קבצים</system:String>
|
||||
<system:String x:Key="fileManager_learnMore">למד עוד</system:String>
|
||||
<system:String x:Key="fileManager_tips">אנא ציין את מיקום הקובץ של מנהל הקבצים שבו אתה משתמש והוסף ארגומנטים כנדרש. "%d" מייצג את נתיב התיקייה שיש לפתוח, ומשמש בשדה ארגומנט לתיקייה ובפקודות לפתיחת תיקיות מסוימות. "%f" מייצג את נתיב הקובץ שיש לפתוח, ומשמש בשדה ארגומנט לקובץ ובפקודות לפתיחת קבצים מסוימים.</system:String>
|
||||
<system:String x:Key="fileManager_tips2">לדוגמה, אם מנהל הקבצים משתמש בפקודה כגון "totalcmd.exe /A c:\windows" כדי לפתוח את התיקייה c:\windows, נתיב מנהל הקבצים יהיה totalcmd.exe, והארגומנט לתיקייה יהיה /A "%d". מנהלי קבצים מסוימים, כגון QTTabBar, עשויים לדרוש רק ציון נתיב, במקרה כזה השתמש ב-"%d" כנתיב מנהל הקבצים והשאר את שאר השדות ריקים.</system:String>
|
||||
<system:String x:Key="fileManager_name">מנהל קבצים</system:String>
|
||||
|
|
@ -311,6 +379,8 @@
|
|||
<system:String x:Key="fileManager_path">נתיב מנהל קבצים</system:String>
|
||||
<system:String x:Key="fileManager_directory_arg">ארגומנט לתיקייה</system:String>
|
||||
<system:String x:Key="fileManager_file_arg">ארגומנט לקובץ</system:String>
|
||||
<system:String x:Key="fileManagerPathNotFound">לא ניתן היה לאתר את מנהל הקבצים '{0}' ב-'{1}'. האם ברצונך להמשיך?</system:String>
|
||||
<system:String x:Key="fileManagerPathError">שגיאת נתיב למנהל הקבצים</system:String>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">דפדפן ברירת מחדל</system:String>
|
||||
|
|
@ -335,9 +405,19 @@
|
|||
<system:String x:Key="cannotFindSpecifiedPlugin">לא ניתן למצוא את התוסף שצוין</system:String>
|
||||
<system:String x:Key="newActionKeywordsCannotBeEmpty">מילת הפעולה החדשה לא יכולה להיות ריקה</system:String>
|
||||
<system:String x:Key="newActionKeywordsHasBeenAssigned">מילת הפעולה החדשה כבר מוקצה לתוסף אחר, אנא בחר אחת שונה</system:String>
|
||||
<system:String x:Key="newActionKeywordsSameAsOld">מילת הפעולה החדשה זהה לישנה, נא לבחור מילת פעולה שונה</system:String>
|
||||
<system:String x:Key="success">הצליח</system:String>
|
||||
<system:String x:Key="completedSuccessfully">הושלם בהצלחה</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">הזן את מילת הפעולה שברצונך להשתמש בה להפעלת התוסף. השתמש ב-* אם אינך רוצה לציין מילה כלשהי, והתוסף יופעל ללא צורך במילת פעולה.</system:String>
|
||||
<system:String x:Key="failedToCopy">ההעתקה נכשלה</system:String>
|
||||
<system:String x:Key="actionkeyword_tips">הזן את מילות הפעולה שבהן תרצה להשתמש כדי להפעיל את התוסף, והשתמש ברווחים כדי להפריד ביניהן. השתמש ב-* אם אינך רוצה להגדיר כלל, והתוסף יופעל ללא מילות פעולה.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="searchDelayTimeTitle">הגדרת זמן עיכוב החיפוש</system:String>
|
||||
<system:String x:Key="searchDelayTimeTips">הזן את זמן עיכוב החיפוש בשניות שבו אתה רוצה להשתמש עבור התוסף. השאר ריק אם אינך רוצה לציין, והתוסף ישתמש בזמן ברירת המחדל לעיכוב חיפוש.</system:String>
|
||||
|
||||
<!-- Search Delay Settings Dialog -->
|
||||
<system:String x:Key="homeTitle">דף הבית</system:String>
|
||||
<system:String x:Key="homeTips">הפעל את מצב דף הבית של התוסף אם ברצונך להציג את תוצאות התוסף כאשר השאילתה ריקה.</system:String>
|
||||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">מקש קיצור לשאילתה מותאמת אישית</system:String>
|
||||
|
|
@ -392,6 +472,14 @@
|
|||
<system:String x:Key="reportWindow_upload_log">1. העלה קובץ יומן: {0}</system:String>
|
||||
<system:String x:Key="reportWindow_copy_below">2. העתק את הודעת החריגה למטה</system:String>
|
||||
|
||||
<!-- File Open Error -->
|
||||
<system:String x:Key="fileManagerNotFoundTitle">שגיאת מנהל הקבצים</system:String>
|
||||
<system:String x:Key="fileManagerNotFound">
|
||||
לא ניתן היה למצוא את מנהל הקבצים שצוין. אנא בדוק את ההגדרה של מנהל קבצים מותאם אישית תחת הגדרות > כללי.
|
||||
</system:String>
|
||||
<system:String x:Key="errorTitle">שגיאה</system:String>
|
||||
<system:String x:Key="folderOpenError">אירעה שגיאה בעת פתיחת התיקייה. {0}</system:String>
|
||||
|
||||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">אנא המתן...</system:String>
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue