Merge branch 'dev' of github.com:Flow-Launcher/Flow.Launcher into jsonrpc_v2
27
.github/ISSUE_TEMPLATE/bug-report.yaml
vendored
|
|
@ -10,11 +10,18 @@ body:
|
|||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checks
|
||||
label: Check New Issue
|
||||
options:
|
||||
- label: >
|
||||
I have checked that this issue has not already been reported.
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Check Latest Version
|
||||
options:
|
||||
- label: >
|
||||
I am using the latest version of Flow Launcher.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Problem Description
|
||||
|
|
@ -41,25 +48,25 @@ body:
|
|||
- type: input
|
||||
attributes:
|
||||
label: Flow Launcher Version
|
||||
description: Go to "Settings" => "About".
|
||||
value: v1.8.3
|
||||
description: Go to "Settings" => "About". If you are using a prerelease version please append the build number.
|
||||
placeholder: "Example: 1.11.0"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: Windows Build Number
|
||||
description: Run "ver" at CMD (command prompt).
|
||||
value: 10.0.19043.1288
|
||||
description: Run "ver" at CMD (command prompt) or go to Windows Settings -> Systems -> About.
|
||||
placeholder: "Example: 10.0.19043.1288"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Error Log
|
||||
description: >
|
||||
Log file place:
|
||||
|
||||
- The latest version place: `%AppData%\FlowLauncher\Logs\<version>\<date>.txt`
|
||||
|
||||
- For portable mode: `%LocalAppData%\FlowLauncher\<App-Version>\UserData\Logs\<version>\<date>.txt`
|
||||
From flow type 'open log location' and find log file with the corresponding date containing the error info.
|
||||
value: >
|
||||
<details>
|
||||
|
||||
|
|
|
|||
17
.github/actions/spelling/README.md
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# check-spelling/check-spelling configuration
|
||||
|
||||
File | Purpose | Format | Info
|
||||
-|-|-|-
|
||||
[dictionary.txt](dictionary.txt) | Replacement dictionary (creating this file will override the default dictionary) | one word per line | [dictionary](https://github.com/check-spelling/check-spelling/wiki/Configuration#dictionary)
|
||||
[allow.txt](allow.txt) | Add words to the dictionary | one word per line (only letters and `'`s allowed) | [allow](https://github.com/check-spelling/check-spelling/wiki/Configuration#allow)
|
||||
[reject.txt](reject.txt) | Remove words from the dictionary (after allow) | grep pattern matching whole dictionary words | [reject](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-reject)
|
||||
[excludes.txt](excludes.txt) | Files to ignore entirely | perl regular expression | [excludes](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-excludes)
|
||||
[only.txt](only.txt) | Only check matching files (applied after excludes) | perl regular expression | [only](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-only)
|
||||
[patterns.txt](patterns.txt) | Patterns to ignore from checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[candidate.patterns](candidate.patterns) | Patterns that might be worth adding to [patterns.txt](patterns.txt) | perl regular expression with optional comment block introductions (all matches will be suggested) | [candidates](https://github.com/check-spelling/check-spelling/wiki/Feature:-Suggest-patterns)
|
||||
[line_forbidden.patterns](line_forbidden.patterns) | Patterns to flag in checked lines | perl regular expression (order matters, first match wins) | [patterns](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-patterns)
|
||||
[expect.txt](expect.txt) | Expected words that aren't in the dictionary | one word per line (sorted, alphabetically) | [expect](https://github.com/check-spelling/check-spelling/wiki/Configuration#expect)
|
||||
[advice.md](advice.md) | Supplement for GitHub comment when unrecognized words are found | GitHub Markdown | [advice](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice)
|
||||
|
||||
Note: you can replace any of these files with a directory by the same name (minus the suffix)
|
||||
and then include multiple files inside that directory (with that suffix) to merge multiple files together.
|
||||
25
.github/actions/spelling/advice.md
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<!-- See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples%3A-advice --> <!-- markdownlint-disable MD033 MD041 -->
|
||||
<details><summary>If the flagged items are :exploding_head: false positives</summary>
|
||||
|
||||
If items relate to a ...
|
||||
* binary file (or some other file you wouldn't want to check at all).
|
||||
|
||||
Please add a file path to the `excludes.txt` file matching the containing file.
|
||||
|
||||
File paths are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your files.
|
||||
|
||||
`^` refers to the file's path from the root of the repository, so `^README\.md$` would exclude [README.md](
|
||||
../tree/HEAD/README.md) (on whichever branch you're using).
|
||||
|
||||
* well-formed pattern.
|
||||
|
||||
If you can write a [pattern](https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns) that would match it,
|
||||
try adding it to the `patterns.txt` file.
|
||||
|
||||
Patterns are Perl 5 Regular Expressions - you can [test](
|
||||
https://www.regexplanet.com/advanced/perl/) yours before committing to verify it will match your lines.
|
||||
|
||||
Note that patterns can't match multiline strings.
|
||||
|
||||
</details>
|
||||
5
.github/actions/spelling/allow.txt
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
github
|
||||
https
|
||||
ssh
|
||||
ubuntu
|
||||
runcount
|
||||
522
.github/actions/spelling/candidate.patterns
vendored
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
# marker to ignore all code on line
|
||||
^.*/\* #no-spell-check-line \*/.*$
|
||||
# marker for ignoring a comment to the end of the line
|
||||
// #no-spell-check.*$
|
||||
|
||||
# patch hunk comments
|
||||
^\@\@ -\d+(?:,\d+|) \+\d+(?:,\d+|) \@\@ .*
|
||||
# git index header
|
||||
index [0-9a-z]{7,40}\.\.[0-9a-z]{7,40}
|
||||
|
||||
# cid urls
|
||||
(['"])cid:.*?\g{-1}
|
||||
|
||||
# data url in parens
|
||||
\(data:[^)]*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})[^)]*\)
|
||||
# data url in quotes
|
||||
([`'"])data:.*?(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,}).*\g{-1}
|
||||
# data url
|
||||
data:[-a-zA-Z=;:/0-9+]*,\S*
|
||||
|
||||
# mailto urls
|
||||
mailto:[-a-zA-Z=;:/?%&0-9+@.]{3,}
|
||||
|
||||
# magnet urls
|
||||
magnet:[?=:\w]+
|
||||
|
||||
# magnet urls
|
||||
"magnet:[^"]+"
|
||||
|
||||
# obs:
|
||||
"obs:[^"]*"
|
||||
|
||||
# The `\b` here means a break, it's the fancy way to handle urls, but it makes things harder to read
|
||||
# In this examples content, I'm using a number of different ways to match things to show various approaches
|
||||
# asciinema
|
||||
\basciinema\.org/a/[0-9a-zA-Z]+
|
||||
|
||||
# apple
|
||||
\bdeveloper\.apple\.com/[-\w?=/]+
|
||||
# Apple music
|
||||
\bembed\.music\.apple\.com/fr/playlist/usr-share/[-\w.]+
|
||||
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
# appveyor project
|
||||
\bci\.appveyor\.com/project/(?:[^/\s"]*/){2}builds?/\d+/job/[0-9a-z]+
|
||||
|
||||
# Amazon
|
||||
|
||||
# Amazon
|
||||
\bamazon\.com/[-\w]+/(?:dp/[0-9A-Z]+|)
|
||||
# AWS S3
|
||||
\b\w*\.s3[^.]*\.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS execute-api
|
||||
\b[0-9a-z]{10}\.execute-api\.[-0-9a-z]+\.amazonaws\.com\b
|
||||
# AWS ELB
|
||||
\b\w+\.[-0-9a-z]+\.elb\.amazonaws\.com\b
|
||||
# AWS SNS
|
||||
\bsns\.[-0-9a-z]+.amazonaws\.com/[-\w/&#%_?:=]*
|
||||
# AWS VPC
|
||||
vpc-\w+
|
||||
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
# YouTube music
|
||||
\bmusic\.youtube\.com/youtubei/v1/browse(?:[?&]\w+=[-a-zA-Z0-9?&=_]*)
|
||||
# YouTube tag
|
||||
<\s*youtube\s+id=['"][-a-zA-Z0-9?_]*['"]
|
||||
# YouTube image
|
||||
\bimg\.youtube\.com/vi/[-a-zA-Z0-9?&=_]*
|
||||
# Google Accounts
|
||||
\baccounts.google.com/[-_/?=.:;+%&0-9a-zA-Z]*
|
||||
# Google Analytics
|
||||
\bgoogle-analytics\.com/collect.[-0-9a-zA-Z?%=&_.~]*
|
||||
# Google APIs
|
||||
\bgoogleapis\.(?:com|dev)/[a-z]+/(?:v\d+/|)[a-z]+/[-@:./?=\w+|&]+
|
||||
# Google Storage
|
||||
\b[-a-zA-Z0-9.]*\bstorage\d*\.googleapis\.com(?:/\S*|)
|
||||
# Google Calendar
|
||||
\bcalendar\.google\.com/calendar(?:/u/\d+|)/embed\?src=[@./?=\w&%]+
|
||||
\w+\@group\.calendar\.google\.com\b
|
||||
# Google DataStudio
|
||||
\bdatastudio\.google\.com/(?:(?:c/|)u/\d+/|)(?:embed/|)(?:open|reporting|datasources|s)/[-0-9a-zA-Z]+(?:/page/[-0-9a-zA-Z]+|)
|
||||
# The leading `/` here is as opposed to the `\b` above
|
||||
# ... a short way to match `https://` or `http://` since most urls have one of those prefixes
|
||||
# Google Docs
|
||||
/docs\.google\.com/[a-z]+/(?:ccc\?key=\w+|(?:u/\d+|d/(?:e/|)[0-9a-zA-Z_-]+/)?(?:edit\?[-\w=#.]*|/\?[\w=&]*|))
|
||||
# Google Drive
|
||||
\bdrive\.google\.com/(?:file/d/|open)[-0-9a-zA-Z_?=]*
|
||||
# Google Groups
|
||||
\bgroups\.google\.com/(?:(?:forum/#!|d/)(?:msg|topics?|searchin)|a)/[^/\s"]+/[-a-zA-Z0-9$]+(?:/[-a-zA-Z0-9]+)*
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
# Google themes
|
||||
themes\.googleusercontent\.com/static/fonts/[^/\s"]+/v\d+/[^.]+.
|
||||
# Google CDN
|
||||
\bclients2\.google(?:usercontent|)\.com[-0-9a-zA-Z/.]*
|
||||
# Goo.gl
|
||||
/goo\.gl/[a-zA-Z0-9]+
|
||||
# Google Chrome Store
|
||||
\bchrome\.google\.com/webstore/detail/[-\w]*(?:/\w*|)
|
||||
# Google Books
|
||||
\bgoogle\.(?:\w{2,4})/books(?:/\w+)*\?[-\w\d=&#.]*
|
||||
# Google Fonts
|
||||
\bfonts\.(?:googleapis|gstatic)\.com/[-/?=:;+&0-9a-zA-Z]*
|
||||
# Google Forms
|
||||
\bforms\.gle/\w+
|
||||
# Google Scholar
|
||||
\bscholar\.google\.com/citations\?user=[A-Za-z0-9_]+
|
||||
# Google Colab Research Drive
|
||||
\bcolab\.research\.google\.com/drive/[-0-9a-zA-Z_?=]*
|
||||
|
||||
# GitHub SHAs (api)
|
||||
\bapi.github\.com/repos(?:/[^/\s"]+){3}/[0-9a-f]+\b
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
# GitHub SHAs
|
||||
\bgithub\.com(?:/[^/\s"]+){2}[@#][0-9a-f]+\b
|
||||
# GitHub wiki
|
||||
\bgithub\.com/(?:[^/]+/){2}wiki/(?:(?:[^/]+/|)_history|[^/]+(?:/_compare|)/[0-9a-f.]{40,})\b
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
# githubassets
|
||||
\bgithubassets.com/[0-9a-f]+(?:[-/\w.]+)
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
# git.io
|
||||
\bgit\.io/[0-9a-zA-Z]+
|
||||
# GitHub JSON
|
||||
"node_id": "[-a-zA-Z=;:/0-9+]*"
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
# GHSA
|
||||
GHSA(?:-[0-9a-z]{4}){3}
|
||||
|
||||
# GitLab commit
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/commit/[0-9a-f]{7,16}#[0-9a-f]{40}\b
|
||||
# GitLab merge requests
|
||||
\bgitlab\.[^/\s"]*/\S+/\S+/-/merge_requests/\d+/diffs#[0-9a-f]{40}\b
|
||||
# GitLab uploads
|
||||
\bgitlab\.[^/\s"]*/uploads/[-a-zA-Z=;:/0-9+]*
|
||||
# GitLab commits
|
||||
\bgitlab\.[^/\s"]*/(?:[^/\s"]+/){2}commits?/[0-9a-f]+\b
|
||||
|
||||
# binanace
|
||||
accounts.binance.com/[a-z/]*oauth/authorize\?[-0-9a-zA-Z&%]*
|
||||
|
||||
# bitbucket diff
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}diff(?:stat|)(?:/[^/\s"]+){2}:[0-9a-f]+
|
||||
# bitbucket repositories commits
|
||||
\bapi\.bitbucket\.org/\d+\.\d+/repositories/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
# bitbucket commits
|
||||
\bbitbucket\.org/(?:[^/\s"]+/){2}commits?/[0-9a-f]+
|
||||
|
||||
# bit.ly
|
||||
\bbit\.ly/\w+
|
||||
|
||||
# bitrise
|
||||
\bapp\.bitrise\.io/app/[0-9a-f]*/[\w.?=&]*
|
||||
|
||||
# bootstrapcdn.com
|
||||
\bbootstrapcdn\.com/[-./\w]+
|
||||
|
||||
# cdn.cloudflare.com
|
||||
\bcdnjs\.cloudflare\.com/[./\w]+
|
||||
|
||||
# circleci
|
||||
\bcircleci\.com/gh(?:/[^/\s"]+){1,5}.[a-z]+\?[-0-9a-zA-Z=&]+
|
||||
|
||||
# gitter
|
||||
\bgitter\.im(?:/[^/\s"]+){2}\?at=[0-9a-f]+
|
||||
|
||||
# gravatar
|
||||
\bgravatar\.com/avatar/[0-9a-f]+
|
||||
|
||||
# ibm
|
||||
[a-z.]*ibm\.com/[-_#=:%!?~.\\/\d\w]*
|
||||
|
||||
# imgur
|
||||
\bimgur\.com/[^.]+
|
||||
|
||||
# Internet Archive
|
||||
\barchive\.org/web/\d+/(?:[-\w.?,'/\\+&%$#_:]*)
|
||||
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# Disqus
|
||||
\bdisqus\.com/[-\w/%.()!?&=_]*
|
||||
|
||||
# medium link
|
||||
\blink\.medium\.com/[a-zA-Z0-9]+
|
||||
# medium
|
||||
\bmedium\.com/\@?[^/\s"]+/[-\w]+
|
||||
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
# powerbi
|
||||
\bapp\.powerbi\.com/reportEmbed/[^"' ]*
|
||||
# vs devops
|
||||
\bvisualstudio.com(?::443|)/[-\w/?=%&.]*
|
||||
# microsoft store
|
||||
\bmicrosoft\.com/store/apps/\w+
|
||||
|
||||
# mvnrepository.com
|
||||
\bmvnrepository\.com/[-0-9a-z./]+
|
||||
|
||||
# now.sh
|
||||
/[0-9a-z-.]+\.now\.sh\b
|
||||
|
||||
# oracle
|
||||
\bdocs\.oracle\.com/[-0-9a-zA-Z./_?#&=]*
|
||||
|
||||
# chromatic.com
|
||||
/\S+.chromatic.com\S*[")]
|
||||
|
||||
# codacy
|
||||
\bapi\.codacy\.com/project/badge/Grade/[0-9a-f]+
|
||||
|
||||
# compai
|
||||
\bcompai\.pub/v1/png/[0-9a-f]+
|
||||
|
||||
# mailgun api
|
||||
\.api\.mailgun\.net/v3/domains/[0-9a-z]+\.mailgun.org/messages/[0-9a-zA-Z=@]*
|
||||
# mailgun
|
||||
\b[0-9a-z]+.mailgun.org
|
||||
|
||||
# /message-id/
|
||||
/message-id/[-\w@./%]+
|
||||
|
||||
# Reddit
|
||||
\breddit\.com/r/[/\w_]*
|
||||
|
||||
# requestb.in
|
||||
\brequestb\.in/[0-9a-z]+
|
||||
|
||||
# sched
|
||||
\b[a-z0-9]+\.sched\.com\b
|
||||
|
||||
# Slack url
|
||||
slack://[a-zA-Z0-9?&=]+
|
||||
# Slack
|
||||
\bslack\.com/[-0-9a-zA-Z/_~?&=.]*
|
||||
# Slack edge
|
||||
\bslack-edge\.com/[-a-zA-Z0-9?&=%./]+
|
||||
# Slack images
|
||||
\bslack-imgs\.com/[-a-zA-Z0-9?&=%.]+
|
||||
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# Sentry
|
||||
[0-9a-f]{32}\@o\d+\.ingest\.sentry\.io\b
|
||||
|
||||
# Twitter markdown
|
||||
\[\@[^[/\]:]*?\]\(https://twitter.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)\)
|
||||
# Twitter hashtag
|
||||
\btwitter\.com/hashtag/[\w?_=&]*
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
# Twitter profile images
|
||||
\btwimg\.com/profile_images/[_\w./]*
|
||||
# Twitter media
|
||||
\btwimg\.com/media/[-_\w./?=]*
|
||||
# Twitter link shortened
|
||||
\bt\.co/\w+
|
||||
|
||||
# facebook
|
||||
\bfburl\.com/[0-9a-z_]+
|
||||
# facebook CDN
|
||||
\bfbcdn\.net/[\w/.,]*
|
||||
# facebook watch
|
||||
\bfb\.watch/[0-9A-Za-z]+
|
||||
|
||||
# dropbox
|
||||
\bdropbox\.com/sh?/[^/\s"]+/[-0-9A-Za-z_.%?=&;]+
|
||||
|
||||
# ipfs protocol
|
||||
ipfs://[0-9a-z]*
|
||||
# ipfs url
|
||||
/ipfs/[0-9a-z]*
|
||||
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# loom
|
||||
\bloom\.com/embed/[0-9a-f]+
|
||||
|
||||
# regex101
|
||||
\bregex101\.com/r/[^/\s"]+/\d+
|
||||
|
||||
# figma
|
||||
\bfigma\.com/file(?:/[0-9a-zA-Z]+/)+
|
||||
|
||||
# freecodecamp.org
|
||||
\bfreecodecamp\.org/[-\w/.]+
|
||||
|
||||
# image.tmdb.org
|
||||
\bimage\.tmdb\.org/[/\w.]+
|
||||
|
||||
# mermaid
|
||||
\bmermaid\.ink/img/[-\w]+|\bmermaid-js\.github\.io/mermaid-live-editor/#/edit/[-\w]+
|
||||
|
||||
# Wikipedia
|
||||
\ben\.wikipedia\.org/wiki/[-\w%.#]+
|
||||
|
||||
# gitweb
|
||||
[^"\s]+/gitweb/\S+;h=[0-9a-f]+
|
||||
|
||||
# HyperKitty lists
|
||||
/archives/list/[^@/]+\@[^/\s"]*/message/[^/\s"]*/
|
||||
|
||||
# lists
|
||||
/thread\.html/[^"\s]+
|
||||
|
||||
# list-management
|
||||
\blist-manage\.com/subscribe(?:[?&](?:u|id)=[0-9a-f]+)+
|
||||
|
||||
# kubectl.kubernetes.io/last-applied-configuration
|
||||
"kubectl.kubernetes.io/last-applied-configuration": ".*"
|
||||
|
||||
# pgp
|
||||
\bgnupg\.net/pks/lookup[?&=0-9a-zA-Z]*
|
||||
|
||||
# Spotify
|
||||
\bopen\.spotify\.com/embed/playlist/\w+
|
||||
|
||||
# Mastodon
|
||||
\bmastodon\.[-a-z.]*/(?:media/|\@)[?&=0-9a-zA-Z_]*
|
||||
|
||||
# scastie
|
||||
\bscastie\.scala-lang\.org/[^/]+/\w+
|
||||
|
||||
# images.unsplash.com
|
||||
\bimages\.unsplash\.com/(?:(?:flagged|reserve)/|)[-\w./%?=%&.;]+
|
||||
|
||||
# pastebin
|
||||
\bpastebin\.com/[\w/]+
|
||||
|
||||
# heroku
|
||||
\b\w+\.heroku\.com/source/archive/\w+
|
||||
|
||||
# quip
|
||||
\b\w+\.quip\.com/\w+(?:(?:#|/issues/)\w+)?
|
||||
|
||||
# badgen.net
|
||||
\bbadgen\.net/badge/[^")\]'\s]+
|
||||
|
||||
# statuspage.io
|
||||
\w+\.statuspage\.io\b
|
||||
|
||||
# media.giphy.com
|
||||
\bmedia\.giphy\.com/media/[^/]+/[\w.?&=]+
|
||||
|
||||
# tinyurl
|
||||
\btinyurl\.com/\w+
|
||||
|
||||
# getopts
|
||||
\bgetopts\s+(?:"[^"]+"|'[^']+')
|
||||
|
||||
# ANSI color codes
|
||||
(?:\\(?:u00|x)1b|\x1b)\[\d+(?:;\d+|)m
|
||||
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
# IPv6
|
||||
\b(?:[0-9a-fA-F]{0,4}:){3,7}[0-9a-fA-F]{0,4}\b
|
||||
# c99 hex digits (not the full format, just one I've seen)
|
||||
0x[0-9a-fA-F](?:\.[0-9a-fA-F]*|)[pP]
|
||||
# Punycode
|
||||
\bxn--[-0-9a-z]+
|
||||
# sha
|
||||
sha\d+:[0-9]*[a-f]{3,}[0-9a-f]*
|
||||
# sha-... -- uses a fancy capture
|
||||
(['"]|")[0-9a-f]{40,}\g{-1}
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
# hex in url queries
|
||||
=[0-9a-fA-F]*?(?:[A-F]{3,}|[a-f]{3,})[0-9a-fA-F]*?&
|
||||
# ssh
|
||||
(?:ssh-\S+|-nistp256) [-a-zA-Z=;:/0-9+]{12,}
|
||||
|
||||
# PGP
|
||||
\b(?:[0-9A-F]{4} ){9}[0-9A-F]{4}\b
|
||||
# GPG keys
|
||||
\b(?:[0-9A-F]{4} ){5}(?: [0-9A-F]{4}){5}\b
|
||||
# Well known gpg keys
|
||||
.well-known/openpgpkey/[\w./]+
|
||||
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
# integrity
|
||||
integrity="sha\d+-[-a-zA-Z=;:/0-9+]{40,}"
|
||||
|
||||
# https://www.gnu.org/software/groff/manual/groff.html
|
||||
# man troff content
|
||||
\\f[BCIPR]
|
||||
# '
|
||||
\\\(aq
|
||||
|
||||
# .desktop mime types
|
||||
^MimeTypes?=.*$
|
||||
# .desktop localized entries
|
||||
^[A-Z][a-z]+\[[a-z]+\]=.*$
|
||||
# Localized .desktop content
|
||||
Name\[[^\]]+\]=.*
|
||||
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# crypt
|
||||
"\$2[ayb]\$.{56}"
|
||||
|
||||
# scrypt / argon
|
||||
\$(?:scrypt|argon\d+[di]*)\$\S+
|
||||
|
||||
# Input to GitHub JSON
|
||||
content: "[-a-zA-Z=;:/0-9+]*="
|
||||
|
||||
# Python stringprefix / binaryprefix
|
||||
# Note that there's a high false positive rate, remove the `?=` and search for the regex to see if the matches seem like reasonable strings
|
||||
(?<!')\b(?:B|BR|Br|F|FR|Fr|R|RB|RF|Rb|Rf|U|UR|Ur|b|bR|br|f|fR|fr|r|rB|rF|rb|rf|u|uR|ur)'(?:[A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})
|
||||
|
||||
# Regular expressions for (P|p)assword
|
||||
\([A-Z]\|[a-z]\)[a-z]+
|
||||
|
||||
# JavaScript regular expressions
|
||||
# javascript test regex
|
||||
/.*/[gim]*\.test\(
|
||||
# javascript match regex
|
||||
\.match\(/[^/\s"]*/[gim]*\s*
|
||||
# javascript match regex
|
||||
\.match\(/\\[b].*?/[gim]*\s*\)(?:;|$)
|
||||
# javascript regex
|
||||
^\s*/\\[b].*/[gim]*\s*(?:\)(?:;|$)|,$)
|
||||
# javascript replace regex
|
||||
\.replace\(/[^/\s"]*/[gim]*\s*,
|
||||
|
||||
# Go regular expressions
|
||||
regexp?\.MustCompile\(`[^`]*`\)
|
||||
|
||||
# sed regular expressions
|
||||
sed 's/(?:[^/]*?[a-zA-Z]{3,}[^/]*?/){2}
|
||||
|
||||
# go install
|
||||
go install(?:\s+[a-z]+\.[-@\w/.]+)+
|
||||
|
||||
# kubernetes pod status lists
|
||||
# https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-phase
|
||||
\w+(?:-\w+)+\s+\d+/\d+\s+(?:Running|Pending|Succeeded|Failed|Unknown)\s+
|
||||
|
||||
# kubectl - pods in CrashLoopBackOff
|
||||
\w+-[0-9a-f]+-\w+\s+\d+/\d+\s+CrashLoopBackOff\s+
|
||||
|
||||
# kubernetes object suffix
|
||||
-[0-9a-f]{10}-\w{5}\s
|
||||
|
||||
# posthog secrets
|
||||
posthog\.init\((['"])phc_[^"',]+\g{-1},
|
||||
|
||||
# xcode
|
||||
|
||||
# xcodeproject scenes
|
||||
(?:Controller|ID|id)="\w{3}-\w{2}-\w{3}"
|
||||
|
||||
# xcode api botches
|
||||
customObjectInstantitationMethod
|
||||
|
||||
# font awesome classes
|
||||
\.fa-[-a-z0-9]+
|
||||
|
||||
# Update Lorem based on your content (requires `ge` and `w` from https://github.com/jsoref/spelling; and `review` from https://github.com/check-spelling/check-spelling/wiki/Looking-for-items-locally )
|
||||
# grep '^[^#].*lorem' .github/actions/spelling/patterns.txt|perl -pne 's/.*i..\?://;s/\).*//' |tr '|' "\n"|sort -f |xargs -n1 ge|perl -pne 's/^[^:]*://'|sort -u|w|sed -e 's/ .*//'|w|review -
|
||||
# Warning, while `(?i)` is very neat and fancy, if you have some binary files that aren't proper unicode, you might run into:
|
||||
## Operation "substitution (s///)" returns its argument for non-Unicode code point 0x1C19AE (the code point will vary).
|
||||
## You could manually change `(?i)X...` to use `[Xx]...`
|
||||
## or you could add the files to your `excludes` file (a version after 0.0.19 should identify the file path)
|
||||
# Lorem
|
||||
(?:\w|\s|[,.])*\b(?i)(?:amet|consectetur|cursus|dolor|eros|ipsum|lacus|libero|ligula|lorem|magna|neque|nulla|suscipit|tempus)\b(?:\w|\s|[,.])*
|
||||
|
||||
# Non-English
|
||||
[a-zA-Z]*[ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź][a-zA-Z]{3}[a-zA-ZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýÿĀāŁłŃńŅņŒœŚśŠšŜŝŸŽžź]*
|
||||
|
||||
# French
|
||||
# This corpus only had capital letters, but you probably want lowercase ones as well.
|
||||
\b[LN]'+[a-z]{2,}\b
|
||||
|
||||
# latex
|
||||
\\(?:n(?:ew|ormal|osub)|r(?:enew)|t(?:able(?:of|)|he|itle))(?=[a-z]+)
|
||||
|
||||
# the negative lookahead here is to allow catching 'templatesz' as a misspelling
|
||||
# but to otherwise recognize a Windows path with \templates\foo.template or similar:
|
||||
\\(?:necessary|r(?:eport|esolve[dr]?|esult)|t(?:arget|emplates?))(?![a-z])
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
# Note that the next example is no longer necessary if you are using
|
||||
# to match a string starting with a `#`, use a character-class:
|
||||
[#]backwards
|
||||
# version suffix <word>v#
|
||||
(?:(?<=[A-Z]{2})V|(?<=[a-z]{2}|[A-Z]{2})v)\d+(?:\b|(?=[a-zA-Z_]))
|
||||
# Compiler flags (Scala)
|
||||
(?:^|[\t ,>"'`=(])-J-[DPWXY](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags
|
||||
(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
# Compiler flags (linker)
|
||||
,-B
|
||||
# curl arguments
|
||||
\b(?:\\n|)curl(?:\s+-[a-zA-Z]{1,2}\b)*(?:\s+-[a-zA-Z]{3,})(?:\s+-[a-zA-Z]+)*
|
||||
# set arguments
|
||||
\bset(?:\s+-[abefimouxE]{1,2})*\s+-[abefimouxE]{3,}(?:\s+-[abefimouxE]+)*
|
||||
# tar arguments
|
||||
\b(?:\\n|)g?tar(?:\.exe|)(?:(?:\s+--[-a-zA-Z]+|\s+-[a-zA-Z]+|\s[ABGJMOPRSUWZacdfh-pr-xz]+\b)(?:=[^ ]*|))+
|
||||
# tput arguments -- https://man7.org/linux/man-pages/man5/terminfo.5.html -- technically they can be more than 5 chars long...
|
||||
\btput\s+(?:(?:-[SV]|-T\s*\w+)\s+)*\w{3,5}\b
|
||||
# macOS temp folders
|
||||
/var/folders/\w\w/[+\w]+/(?:T|-Caches-)/
|
||||
73
.github/actions/spelling/excludes.txt
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-excludes
|
||||
(?:^|/)(?i)COPYRIGHT
|
||||
(?:^|/)(?i)LICEN[CS]E
|
||||
(?:^|/)3rdparty/
|
||||
(?:^|/)go\.sum$
|
||||
(?:^|/)package(?:-lock|)\.json$
|
||||
(?:^|/)vendor/
|
||||
\.a$
|
||||
\.ai$
|
||||
\.avi$
|
||||
\.bmp$
|
||||
\.bz2$
|
||||
\.class$
|
||||
\.crt$
|
||||
\.dll$
|
||||
\.docx?$
|
||||
\.drawio$
|
||||
\.DS_Store$
|
||||
\.eot$
|
||||
\.exe$
|
||||
\.gif$
|
||||
\.gitattributes$
|
||||
\.gitignore$
|
||||
\.graffle$
|
||||
\.gz$
|
||||
\.icns$
|
||||
\.ico$
|
||||
\.jar$
|
||||
\.jks$
|
||||
\.jpe?g$
|
||||
\.key$
|
||||
\.lib$
|
||||
\.lock$
|
||||
\.map$
|
||||
\.min\..
|
||||
\.mod$
|
||||
\.mp[34]$
|
||||
\.o$
|
||||
\.ocf$
|
||||
\.otf$
|
||||
\.pdf$
|
||||
\.pem$
|
||||
\.png$
|
||||
\.psd$
|
||||
\.pyc$
|
||||
\.s$
|
||||
\.svgz?$
|
||||
\.tar$
|
||||
\.tiff?$
|
||||
\.ttf$
|
||||
\.wav$
|
||||
\.webm$
|
||||
\.webp$
|
||||
\.woff2?$
|
||||
\.xlsx?$
|
||||
\.zip$
|
||||
^\.github/actions/spelling/
|
||||
^\Q.github/workflows/spelling.yml\E$
|
||||
# Custom
|
||||
(?:^|/)Languages/(?!en\.xaml)
|
||||
Scripts/
|
||||
\.resx$
|
||||
^\QPlugins/Flow.Launcher.Plugin.WindowsSettings/WindowsSettings.json\E$
|
||||
^\QPlugins/Flow.Launcher.Plugin.WebSearch/setting.json\E$
|
||||
(?:^|/)FodyWeavers\.xml
|
||||
.editorconfig
|
||||
ignore$
|
||||
\.ps1$
|
||||
\.yml$
|
||||
\.sln$
|
||||
\.csproj$
|
||||
\.DotSettings$
|
||||
\.targets$
|
||||
90
.github/actions/spelling/expect.txt
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
crowdin
|
||||
DWM
|
||||
workflows
|
||||
wpf
|
||||
actionkeyword
|
||||
stackoverflow
|
||||
Wox
|
||||
flowlauncher
|
||||
Fody
|
||||
stackoverflow
|
||||
IContext
|
||||
IShell
|
||||
IPlugin
|
||||
appveyor
|
||||
netflix
|
||||
youtube
|
||||
appdata
|
||||
Prioritise
|
||||
Segoe
|
||||
Google
|
||||
Customise
|
||||
UWP
|
||||
uwp
|
||||
Bokmal
|
||||
Bokm
|
||||
uninstallation
|
||||
uninstalling
|
||||
voidtools
|
||||
fullscreen
|
||||
hotkeys
|
||||
totalcmd
|
||||
lnk
|
||||
amazonaws
|
||||
mscorlib
|
||||
pythonw
|
||||
dotnet
|
||||
winget
|
||||
jjw24
|
||||
wolframalpha
|
||||
gmail
|
||||
duckduckgo
|
||||
facebook
|
||||
findicon
|
||||
baidu
|
||||
pls
|
||||
websearch
|
||||
qianlifeng
|
||||
userdata
|
||||
srchadmin
|
||||
EWX
|
||||
dlgtext
|
||||
CMD
|
||||
appref-ms
|
||||
appref
|
||||
TSource
|
||||
runas
|
||||
dpi
|
||||
popup
|
||||
ptr
|
||||
pluginindicator
|
||||
TobiasSekan
|
||||
Img
|
||||
img
|
||||
resx
|
||||
bak
|
||||
tmp
|
||||
directx
|
||||
mvvm
|
||||
dlg
|
||||
ddd
|
||||
dddd
|
||||
clearlogfolder
|
||||
ACCENT_ENABLE_TRANSPARENTGRADIENT
|
||||
ACCENT_ENABLE_BLURBEHIND
|
||||
WCA_ACCENT_POLICY
|
||||
HGlobal
|
||||
dopusrt
|
||||
firefox
|
||||
msedge
|
||||
svgc
|
||||
ime
|
||||
zindex
|
||||
txb
|
||||
btn
|
||||
otf
|
||||
searchplugin
|
||||
Noresult
|
||||
wpftk
|
||||
mkv
|
||||
flac
|
||||
62
.github/actions/spelling/line_forbidden.patterns
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# reject `m_data` as there's a certain OS which has evil defines that break things if it's used elsewhere
|
||||
# \bm_data\b
|
||||
|
||||
# If you have a framework that uses `it()` for testing and `fit()` for debugging a specific test,
|
||||
# you might not want to check in code where you were debugging w/ `fit()`, in which case, you might want
|
||||
# to use this:
|
||||
#\bfit\(
|
||||
|
||||
# s.b. GitHub
|
||||
#\bGithub\b
|
||||
|
||||
# s.b. GitLab
|
||||
\bGitlab\b
|
||||
|
||||
# s.b. JavaScript
|
||||
\bJavascript\b
|
||||
|
||||
# s.b. Microsoft
|
||||
\bMicroSoft\b
|
||||
|
||||
# s.b. another
|
||||
\ban[- ]other\b
|
||||
|
||||
# s.b. greater than
|
||||
\bgreater then\b
|
||||
|
||||
# s.b. into
|
||||
\sin to\s
|
||||
|
||||
# s.b. opt-in
|
||||
\sopt in\s
|
||||
|
||||
# s.b. less than
|
||||
\bless then\b
|
||||
|
||||
# s.b. otherwise
|
||||
\bother[- ]wise\b
|
||||
|
||||
# s.b. nonexistent
|
||||
\bnon existing\b
|
||||
\b[Nn]o[nt][- ]existent\b
|
||||
|
||||
# s.b. preexisting
|
||||
[Pp]re[- ]existing
|
||||
|
||||
# s.b. preempt
|
||||
[Pp]re[- ]empt\b
|
||||
|
||||
# s.b. preemptively
|
||||
[Pp]re[- ]emptively
|
||||
|
||||
# s.b. reentrancy
|
||||
[Rr]e[- ]entrancy
|
||||
|
||||
# s.b. reentrant
|
||||
[Rr]e[- ]entrant
|
||||
|
||||
# s.b. workaround(s)
|
||||
\bwork[- ]arounds?\b
|
||||
|
||||
# Reject duplicate words
|
||||
\s([A-Z]{3,}|[A-Z][a-z]{2,}|[a-z]{3,})\s\g{-1}\s
|
||||
117
.github/actions/spelling/patterns.txt
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# See https://github.com/check-spelling/check-spelling/wiki/Configuration-Examples:-patterns
|
||||
|
||||
# Questionably acceptable forms of `in to`
|
||||
# Personally, I prefer `log into`, but people object
|
||||
# https://www.tprteaching.com/log-into-log-in-to-login/
|
||||
\b[Ll]og in to\b
|
||||
|
||||
# acceptable duplicates
|
||||
# ls directory listings
|
||||
[-bcdlpsw](?:[-r][-w][-sx]){3}\s+\d+\s+(\S+)\s+\g{-1}\s+\d+\s+
|
||||
# C types and repeated CSS values
|
||||
\s(center|div|inherit|long|LONG|none|normal|solid|thin|transparent|very)(?: \g{-1})+\s
|
||||
# go templates
|
||||
\s(\w+)\s+\g{-1}\s+\`(?:graphql|json|yaml):
|
||||
# javadoc / .net
|
||||
(?:[\\@](?:groupname|param)|(?:public|private)(?:\s+static|\s+readonly)*)\s+(\w+)\s+\g{-1}\s
|
||||
|
||||
# Commit message -- Signed-off-by and friends
|
||||
^\s*(?:(?:Based-on-patch|Co-authored|Helped|Mentored|Reported|Reviewed|Signed-off)-by|Thanks-to): (?:[^<]*<[^>]*>|[^<]*)\s*$
|
||||
|
||||
# Autogenerated revert commit message
|
||||
^This reverts commit [0-9a-f]{40}\.$
|
||||
|
||||
# ignore long runs of a single character:
|
||||
\b([A-Za-z])\g{-1}{3,}\b
|
||||
|
||||
# Automatically suggested patterns
|
||||
# hit-count: 360 file-count: 108
|
||||
# IServiceProvider
|
||||
\bI(?=(?:[A-Z][a-z]{2,})+\b)
|
||||
|
||||
# hit-count: 297 file-count: 18
|
||||
# uuid:
|
||||
\b[0-9a-fA-F]{8}-(?:[0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}\b
|
||||
|
||||
# hit-count: 138 file-count: 27
|
||||
# hex digits including css/html color classes:
|
||||
(?:[\\0][xX]|\\u|[uU]\+|#x?|\%23)[0-9_a-fA-FgGrR]*?[a-fA-FgGrR]{2,}[0-9_a-fA-FgGrR]*(?:[uUlL]{0,3}|u\d+)\b
|
||||
|
||||
# hit-count: 93 file-count: 28
|
||||
# hex runs
|
||||
\b[0-9a-fA-F]{16,}\b
|
||||
|
||||
# hit-count: 52 file-count: 3
|
||||
# githubusercontent
|
||||
/[-a-z0-9]+\.githubusercontent\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
|
||||
/[-a-z0-9]+\.github\.com/[-a-zA-Z0-9?&=_\/.]*
|
||||
|
||||
# hit-count: 24 file-count: 12
|
||||
# GitHub SHAs (markdown)
|
||||
(?:\[`?[0-9a-f]+`?\]\(https:/|)/(?:www\.|)github\.com(?:/[^/\s"]+){2,}(?:/[^/\s")]+)(?:[0-9a-f]+(?:[-0-9a-zA-Z/#.]*|)\b|)
|
||||
|
||||
# hit-count: 11 file-count: 10
|
||||
# stackexchange -- https://stackexchange.com/feeds/sites
|
||||
\b(?:askubuntu|serverfault|stack(?:exchange|overflow)|superuser).com/(?:questions/\w+/[-\w]+|a/)
|
||||
|
||||
# hit-count: 11 file-count: 8
|
||||
# microsoft
|
||||
\b(?:https?://|)(?:(?:download\.visualstudio|docs|msdn2?|research)\.microsoft|blogs\.msdn)\.com/[-_a-zA-Z0-9()=./%]*
|
||||
|
||||
# hit-count: 2 file-count: 2
|
||||
# Twitter status
|
||||
\btwitter\.com/[^/\s"')]*(?:/status/\d+(?:\?[-_0-9a-zA-Z&=]*|)|)
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# discord
|
||||
/discord(?:app\.com|\.gg)/(?:invite/)?[a-zA-Z0-9]{7,}
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# appveyor api
|
||||
\bci\.appveyor\.com/api/projects/status/[0-9a-z]+
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# gist github
|
||||
\bgist\.github\.com/[^/\s"]+/[0-9a-f]+
|
||||
|
||||
# Automatically suggested patterns
|
||||
# hit-count: 21 file-count: 21
|
||||
# w3
|
||||
\bw3\.org/[-0-9a-zA-Z/#.]+
|
||||
|
||||
# hit-count: 6 file-count: 1
|
||||
# shields.io
|
||||
\bshields\.io/[-\w/%?=&.:+;,]*
|
||||
|
||||
# hit-count: 3 file-count: 2
|
||||
# While you could try to match `http://` and `https://` by using `s?` in `https?://`, sometimes there
|
||||
# YouTube url
|
||||
\b(?:(?:www\.|)youtube\.com|youtu.be)/(?:channel/|embed/|user/|playlist\?list=|watch\?v=|v/|)[-a-zA-Z0-9?&=_%]*
|
||||
|
||||
# hit-count: 2 file-count: 2
|
||||
# Google Maps
|
||||
\bmaps\.google\.com/maps\?[\w&;=]*
|
||||
|
||||
# hit-count: 2 file-count: 2
|
||||
# Contributor
|
||||
\[[^\]]+\]\(https://github\.com/[^/\s"]+\)
|
||||
|
||||
# hit-count: 2 file-count: 1
|
||||
# URL escaped characters
|
||||
\%[0-9A-F][A-F]
|
||||
|
||||
# hit-count: 1 file-count: 1
|
||||
# Compiler flags
|
||||
(?:^|[\t ,"'`=(])-[DPWXYLlf](?=[A-Z]{2,}|[A-Z][a-z]|[a-z]{2,})
|
||||
|
||||
# Localization keys
|
||||
#x:Key="[^"]+"
|
||||
#{DynamicResource [^"]+}
|
||||
|
||||
# html tag
|
||||
<\w+[^>]*>
|
||||
</\w+[^>]*>
|
||||
|
||||
#http/https
|
||||
(?:\b(?:https?|ftp|file)://)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]
|
||||
10
.github/actions/spelling/reject.txt
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
^attache$
|
||||
benefitting
|
||||
occurences?
|
||||
^dependan.*
|
||||
^oer$
|
||||
Sorce
|
||||
^[Ss]pae.*
|
||||
^untill$
|
||||
^untilling$
|
||||
^wether.*
|
||||
21
.github/dependabot.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "nuget" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
ignore:
|
||||
- dependency-name: "squirrel-windows"
|
||||
reviewers:
|
||||
- "jjw24"
|
||||
- "taooceros"
|
||||
- "JohnTheGr8"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
160
.github/workflows/spelling.yml
vendored
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
name: Check Spelling
|
||||
|
||||
# Comment management is handled through a secondary job, for details see:
|
||||
# https://github.com/check-spelling/check-spelling/wiki/Feature%3A-Restricted-Permissions
|
||||
#
|
||||
# `jobs.comment-push` runs when a push is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# (in odd cases, it might actually run just to collapse a comment, but that's fairly rare)
|
||||
# it needs `contents: write` in order to add a comment.
|
||||
#
|
||||
# `jobs.comment-pr` runs when a pull_request is made to a repository and the `jobs.spelling` job needs to make a comment
|
||||
# or collapse a comment (in the case where it had previously made a comment and now no longer needs to show a comment)
|
||||
# it needs `pull-requests: write` in order to manipulate those comments.
|
||||
|
||||
# Updating pull request branches is managed via comment handling.
|
||||
# For details, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-expect-list
|
||||
#
|
||||
# These elements work together to make it happen:
|
||||
#
|
||||
# `on.issue_comment`
|
||||
# This event listens to comments by users asking to update the metadata.
|
||||
#
|
||||
# `jobs.update`
|
||||
# This job runs in response to an issue_comment and will push a new commit
|
||||
# to update the spelling metadata.
|
||||
#
|
||||
# `with.experimental_apply_changes_via_bot`
|
||||
# Tells the action to support and generate messages that enable it
|
||||
# to make a commit to update the spelling metadata.
|
||||
#
|
||||
# `with.ssh_key`
|
||||
# In order to trigger workflows when the commit is made, you can provide a
|
||||
# secret (typically, a write-enabled github deploy key).
|
||||
#
|
||||
# For background, see: https://github.com/check-spelling/check-spelling/wiki/Feature:-Update-with-deploy-key
|
||||
|
||||
on:
|
||||
# push:
|
||||
# branches:
|
||||
# - '**'
|
||||
# - '!l10n_dev'
|
||||
# tags-ignore:
|
||||
# - "**"
|
||||
pull_request_target:
|
||||
branches:
|
||||
- '**'
|
||||
# - '!l10n_dev'
|
||||
tags-ignore:
|
||||
- "**"
|
||||
types:
|
||||
- 'opened'
|
||||
- 'reopened'
|
||||
- 'synchronize'
|
||||
# issue_comment:
|
||||
# types:
|
||||
# - 'created'
|
||||
|
||||
jobs:
|
||||
spelling:
|
||||
name: Check Spelling
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
actions: read
|
||||
security-events: write
|
||||
outputs:
|
||||
followup: ${{ steps.spelling.outputs.followup }}
|
||||
runs-on: ubuntu-latest
|
||||
if: (contains(github.event_name, 'pull_request') && github.head_ref != 'l10n_dev')
|
||||
concurrency:
|
||||
group: spelling-${{ github.event.pull_request.number || github.ref }}
|
||||
# note: If you use only_check_changed_files, you do not want cancel-in-progress
|
||||
cancel-in-progress: false
|
||||
steps:
|
||||
- name: check-spelling
|
||||
id: spelling
|
||||
uses: check-spelling/check-spelling@main
|
||||
with:
|
||||
suppress_push_for_open_pull_request: 1
|
||||
checkout: true
|
||||
check_file_names: 1
|
||||
spell_check_this: check-spelling/spell-check-this@main
|
||||
post_comment: 0
|
||||
use_magic_file: 1
|
||||
experimental_apply_changes_via_bot: 1
|
||||
use_sarif: 0 # to show in pr page
|
||||
extra_dictionary_limit: 10
|
||||
check_commit_messages: commits title description
|
||||
only_check_changed_files: 1
|
||||
check_extra_dictionaries: ''
|
||||
quit_without_error: true
|
||||
extra_dictionaries:
|
||||
cspell:software-terms/src/software-terms.txt
|
||||
cspell:win32/src/win32.txt
|
||||
cspell:php/php.txt
|
||||
cspell:filetypes/filetypes.txt
|
||||
cspell:csharp/csharp.txt
|
||||
cspell:dotnet/dotnet.txt
|
||||
cspell:python/src/python/python-lib.txt
|
||||
cspell:aws/aws.txt
|
||||
cspell:companies/src/companies.txt
|
||||
warnings:
|
||||
binary-file,deprecated-feature,large-file,limited-references,noisy-file,non-alpha-in-dictionary,unexpected-line-ending,whitespace-in-dictionary,minified-file,unsupported-configuration,unrecognized-spelling,no-newline-at-eof
|
||||
|
||||
|
||||
|
||||
# comment-push:
|
||||
# name: Report (Push)
|
||||
# # If your workflow isn't running on push, you can remove this job
|
||||
# runs-on: ubuntu-latest
|
||||
# needs: spelling
|
||||
# permissions:
|
||||
# contents: write
|
||||
# if: (success() || failure()) && needs.spelling.outputs.followup && github.event_name == 'push'
|
||||
# steps:
|
||||
# - name: comment
|
||||
# uses: check-spelling/check-spelling@main
|
||||
# with:
|
||||
# checkout: true
|
||||
# spell_check_this: check-spelling/spell-check-this@main
|
||||
# task: ${{ needs.spelling.outputs.followup }}
|
||||
|
||||
comment-pr:
|
||||
name: Report (PR)
|
||||
# If you workflow isn't running on pull_request*, you can remove this job
|
||||
runs-on: ubuntu-latest
|
||||
needs: spelling
|
||||
permissions:
|
||||
pull-requests: write
|
||||
if: (success() || failure()) && needs.spelling.outputs.followup && contains(github.event_name, 'pull_request')
|
||||
steps:
|
||||
- name: comment
|
||||
uses: check-spelling/check-spelling@main
|
||||
with:
|
||||
checkout: true
|
||||
spell_check_this: check-spelling/spell-check-this@main
|
||||
task: ${{ needs.spelling.outputs.followup }}
|
||||
experimental_apply_changes_via_bot: 1
|
||||
|
||||
# update:
|
||||
# name: Update PR
|
||||
# permissions:
|
||||
# contents: write
|
||||
# pull-requests: write
|
||||
# actions: read
|
||||
# runs-on: ubuntu-latest
|
||||
# if: ${{
|
||||
# github.event_name == 'issue_comment' &&
|
||||
# github.event.issue.pull_request &&
|
||||
# contains(github.event.comment.body, '@check-spelling-bot apply')
|
||||
# }}
|
||||
# concurrency:
|
||||
# group: spelling-update-${{ github.event.issue.number }}
|
||||
# cancel-in-progress: false
|
||||
# steps:
|
||||
# - name: apply spelling updates
|
||||
# uses: check-spelling/check-spelling@main
|
||||
# with:
|
||||
# experimental_apply_changes_via_bot: 1
|
||||
# checkout: true
|
||||
# ssh_key: "${{ secrets.CHECK_SPELLING }}"
|
||||
2
.github/workflows/stale.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
- uses: actions/stale@v7
|
||||
with:
|
||||
stale-issue-message: 'This issue is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 5 days.'
|
||||
days-before-stale: 45
|
||||
|
|
|
|||
15
.github/workflows/winget.yml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
name: Publish to Winget
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
# Action can only be run on windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: Flow-Launcher.Flow-Launcher
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
||||
|
|
@ -47,7 +47,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("|Portable.DisablePortableMode|Error occured while disabling portable mode", e);
|
||||
Log.Exception("|Portable.DisablePortableMode|Error occurred while disabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("|Portable.EnablePortableMode|Error occured while enabling portable mode", e);
|
||||
Log.Exception("|Portable.EnablePortableMode|Error occurred while enabling portable mode", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,7 +187,7 @@ namespace Flow.Launcher.Core.Configuration
|
|||
if (roamingLocationExists && portableLocationExists)
|
||||
{
|
||||
MessageBox.Show(string.Format("Flow Launcher detected your user data exists both in {0} and " +
|
||||
"{1}. {2}{2}Please delete {1} in order to proceed. No changes have occured.",
|
||||
"{1}. {2}{2}Please delete {1} in order to proceed. No changes have occurred.",
|
||||
DataLocation.PortableDataPath, DataLocation.RoamingDataPath, Environment.NewLine));
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,234 @@
|
|||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
public abstract class AbstractPluginEnvironment
|
||||
{
|
||||
internal abstract string Language { get; }
|
||||
|
||||
internal abstract string EnvName { get; }
|
||||
|
||||
internal abstract string EnvPath { get; }
|
||||
|
||||
internal abstract string InstallPath { get; }
|
||||
|
||||
internal abstract string ExecutablePath { get; }
|
||||
|
||||
internal virtual string FileDialogFilter => string.Empty;
|
||||
|
||||
internal abstract string PluginsSettingsFilePath { get; set; }
|
||||
|
||||
internal List<PluginMetadata> PluginMetadataList;
|
||||
|
||||
internal PluginsSettings PluginSettings;
|
||||
|
||||
internal AbstractPluginEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings)
|
||||
{
|
||||
PluginMetadataList = pluginMetadataList;
|
||||
PluginSettings = pluginSettings;
|
||||
}
|
||||
|
||||
internal IEnumerable<PluginPair> Setup()
|
||||
{
|
||||
if (!PluginMetadataList.Any(o => o.Language.Equals(Language, StringComparison.OrdinalIgnoreCase)))
|
||||
return new List<PluginPair>();
|
||||
|
||||
// TODO: Remove. This is backwards compatibility for 1.10.0 release- changed PythonEmbeded to Environments/Python
|
||||
if (Language.Equals(AllowedLanguage.Python, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable"));
|
||||
|
||||
if (!string.IsNullOrEmpty(PluginSettings.PythonDirectory) && PluginSettings.PythonDirectory.StartsWith(Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable")))
|
||||
{
|
||||
InstallEnvironment();
|
||||
PluginSettings.PythonDirectory = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(PluginsSettingsFilePath) && FilesFolders.FileExists(PluginsSettingsFilePath))
|
||||
{
|
||||
// Ensure latest only if user is using Flow's environment setup.
|
||||
if (PluginsSettingsFilePath.StartsWith(EnvPath, StringComparison.OrdinalIgnoreCase))
|
||||
EnsureLatestInstalled(ExecutablePath, PluginsSettingsFilePath, EnvPath);
|
||||
|
||||
return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
|
||||
}
|
||||
|
||||
if (MessageBox.Show($"Flow detected you have installed {Language} plugins, which " +
|
||||
$"will require {EnvName} to run. Would you like to download {EnvName}? " +
|
||||
Environment.NewLine + Environment.NewLine +
|
||||
"Click no if it's already installed, " +
|
||||
$"and you will be prompted to select the folder that contains the {EnvName} executable",
|
||||
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No)
|
||||
{
|
||||
var msg = $"Please select the {EnvName} executable";
|
||||
var selectedFile = string.Empty;
|
||||
|
||||
selectedFile = GetFileFromDialog(msg, FileDialogFilter);
|
||||
|
||||
if (!string.IsNullOrEmpty(selectedFile))
|
||||
PluginsSettingsFilePath = selectedFile;
|
||||
|
||||
// Nothing selected because user pressed cancel from the file dialog window
|
||||
if (string.IsNullOrEmpty(selectedFile))
|
||||
InstallEnvironment();
|
||||
}
|
||||
else
|
||||
{
|
||||
InstallEnvironment();
|
||||
}
|
||||
|
||||
if (FilesFolders.FileExists(PluginsSettingsFilePath))
|
||||
{
|
||||
return SetPathForPluginPairs(PluginsSettingsFilePath, Language);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Unable to set {Language} executable path, please try from Flow's settings (scroll down to the bottom).");
|
||||
Log.Error("PluginsLoader",
|
||||
$"Not able to successfully set {EnvName} path, setting's plugin executable path variable is still an empty string.",
|
||||
$"{Language}Environment");
|
||||
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract void InstallEnvironment();
|
||||
|
||||
private void EnsureLatestInstalled(string expectedPath, string currentPath, string installedDirPath)
|
||||
{
|
||||
if (expectedPath == currentPath)
|
||||
return;
|
||||
|
||||
FilesFolders.RemoveFolderIfExists(installedDirPath);
|
||||
|
||||
InstallEnvironment();
|
||||
|
||||
}
|
||||
|
||||
internal abstract PluginPair CreatePluginPair(string filePath, PluginMetadata metadata);
|
||||
|
||||
private IEnumerable<PluginPair> SetPathForPluginPairs(string filePath, string languageToSet)
|
||||
{
|
||||
var pluginPairs = new List<PluginPair>();
|
||||
|
||||
foreach (var metadata in PluginMetadataList)
|
||||
{
|
||||
if (metadata.Language.Equals(languageToSet, StringComparison.OrdinalIgnoreCase))
|
||||
pluginPairs.Add(CreatePluginPair(filePath, metadata));
|
||||
}
|
||||
|
||||
return pluginPairs;
|
||||
}
|
||||
|
||||
private string GetFileFromDialog(string title, string filter = "")
|
||||
{
|
||||
var dlg = new OpenFileDialog
|
||||
{
|
||||
InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles),
|
||||
Multiselect = false,
|
||||
CheckFileExists = true,
|
||||
CheckPathExists = true,
|
||||
Title = title,
|
||||
Filter = filter
|
||||
};
|
||||
|
||||
var result = dlg.ShowDialog();
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
return dlg.FileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// After app updated while in portable mode or switched between portable/roaming mode,
|
||||
/// need to update each plugin's executable path so user will not be prompted again to reinstall the environments.
|
||||
/// </summary>
|
||||
/// <param name="settings"></param>
|
||||
public static void PreStartPluginExecutablePathUpdate(Settings settings)
|
||||
{
|
||||
if (DataLocation.PortableDataLocationInUse())
|
||||
{
|
||||
// When user is using portable but has moved flow to a different location
|
||||
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName)
|
||||
&& !settings.PluginSettings.PythonExecutablePath.StartsWith(DataLocation.PortableDataPath))
|
||||
{
|
||||
settings.PluginSettings.PythonExecutablePath
|
||||
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath);
|
||||
}
|
||||
|
||||
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName)
|
||||
&& !settings.PluginSettings.NodeExecutablePath.StartsWith(DataLocation.PortableDataPath))
|
||||
{
|
||||
settings.PluginSettings.NodeExecutablePath
|
||||
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath);
|
||||
}
|
||||
|
||||
// When user has switched from roaming to portable
|
||||
if (IsUsingRoamingPath(settings.PluginSettings.PythonExecutablePath))
|
||||
{
|
||||
settings.PluginSettings.PythonExecutablePath
|
||||
= settings.PluginSettings.PythonExecutablePath.Replace(DataLocation.RoamingDataPath, DataLocation.PortableDataPath);
|
||||
}
|
||||
|
||||
if (IsUsingRoamingPath(settings.PluginSettings.NodeExecutablePath))
|
||||
{
|
||||
settings.PluginSettings.NodeExecutablePath
|
||||
= settings.PluginSettings.NodeExecutablePath.Replace(DataLocation.RoamingDataPath, DataLocation.PortableDataPath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsUsingPortablePath(settings.PluginSettings.PythonExecutablePath, DataLocation.PythonEnvironmentName))
|
||||
settings.PluginSettings.PythonExecutablePath
|
||||
= GetUpdatedEnvironmentPath(settings.PluginSettings.PythonExecutablePath);
|
||||
|
||||
if (IsUsingPortablePath(settings.PluginSettings.NodeExecutablePath, DataLocation.NodeEnvironmentName))
|
||||
settings.PluginSettings.NodeExecutablePath
|
||||
= GetUpdatedEnvironmentPath(settings.PluginSettings.NodeExecutablePath);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsUsingPortablePath(string filePath, string pluginEnvironmentName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
return false;
|
||||
|
||||
// DataLocation.PortableDataPath returns the current portable path, this determines if an out
|
||||
// of date path is also a portable path.
|
||||
var portableAppEnvLocation = $"UserData\\{DataLocation.PluginEnvironments}\\{pluginEnvironmentName}";
|
||||
|
||||
return filePath.Contains(portableAppEnvLocation);
|
||||
}
|
||||
|
||||
private static bool IsUsingRoamingPath(string filePath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
return false;
|
||||
|
||||
return filePath.StartsWith(DataLocation.RoamingDataPath);
|
||||
}
|
||||
|
||||
private static string GetUpdatedEnvironmentPath(string filePath)
|
||||
{
|
||||
var index = filePath.IndexOf(DataLocation.PluginEnvironments);
|
||||
|
||||
// get the substring after "Environments" because we can not determine it dynamically
|
||||
var ExecutablePathSubstring = filePath.Substring(index + DataLocation.PluginEnvironments.Count());
|
||||
return $"{DataLocation.PluginEnvironmentsPath}{ExecutablePathSubstring}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
using System.Collections.Generic;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
|
||||
internal class JavaScriptEnvironment : TypeScriptEnvironment
|
||||
{
|
||||
internal override string Language => AllowedLanguage.JavaScript;
|
||||
|
||||
internal JavaScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
using Droplex;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
internal class PythonEnvironment : AbstractPluginEnvironment
|
||||
{
|
||||
internal override string Language => AllowedLanguage.Python;
|
||||
|
||||
internal override string EnvName => DataLocation.PythonEnvironmentName;
|
||||
|
||||
internal override string EnvPath => Path.Combine(DataLocation.PluginEnvironmentsPath, EnvName);
|
||||
|
||||
internal override string InstallPath => Path.Combine(EnvPath, "PythonEmbeddable-v3.8.9");
|
||||
|
||||
internal override string ExecutablePath => Path.Combine(InstallPath, "pythonw.exe");
|
||||
|
||||
internal override string FileDialogFilter => "Python|pythonw.exe";
|
||||
|
||||
internal override string PluginsSettingsFilePath { get => PluginSettings.PythonExecutablePath; set => PluginSettings.PythonExecutablePath = value; }
|
||||
|
||||
internal PythonEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath);
|
||||
|
||||
// Python 3.8.9 is used for Windows 7 compatibility
|
||||
DroplexPackage.Drop(App.python_3_8_9_embeddable, InstallPath).Wait();
|
||||
|
||||
PluginsSettingsFilePath = ExecutablePath;
|
||||
}
|
||||
|
||||
internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata)
|
||||
{
|
||||
return new PluginPair
|
||||
{
|
||||
Plugin = new PythonPlugin(filePath),
|
||||
Metadata = metadata
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
using System.Collections.Generic;
|
||||
using Droplex;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Flow.Launcher.Plugin;
|
||||
using System.IO;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins.Environments
|
||||
{
|
||||
internal class TypeScriptEnvironment : AbstractPluginEnvironment
|
||||
{
|
||||
internal override string Language => AllowedLanguage.TypeScript;
|
||||
|
||||
internal override string EnvName => DataLocation.NodeEnvironmentName;
|
||||
|
||||
internal override string EnvPath => Path.Combine(DataLocation.PluginEnvironmentsPath, EnvName);
|
||||
|
||||
internal override string InstallPath => Path.Combine(EnvPath, "Node-v16.18.0");
|
||||
internal override string ExecutablePath => Path.Combine(InstallPath, "node-v16.18.0-win-x64\\node.exe");
|
||||
|
||||
internal override string PluginsSettingsFilePath { get => PluginSettings.NodeExecutablePath; set => PluginSettings.NodeExecutablePath = value; }
|
||||
|
||||
internal TypeScriptEnvironment(List<PluginMetadata> pluginMetadataList, PluginsSettings pluginSettings) : base(pluginMetadataList, pluginSettings) { }
|
||||
|
||||
internal override void InstallEnvironment()
|
||||
{
|
||||
FilesFolders.RemoveFolderIfExists(InstallPath);
|
||||
|
||||
DroplexPackage.Drop(App.nodejs_16_18_0, InstallPath).Wait();
|
||||
|
||||
PluginsSettingsFilePath = ExecutablePath;
|
||||
}
|
||||
|
||||
internal override PluginPair CreatePluginPair(string filePath, PluginMetadata metadata)
|
||||
{
|
||||
return new PluginPair
|
||||
{
|
||||
Plugin = new NodePlugin(filePath),
|
||||
Metadata = metadata
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,13 +29,13 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
var request = new HttpRequestMessage(HttpMethod.Get, manifestFileUrl);
|
||||
request.Headers.Add("If-None-Match", latestEtag);
|
||||
|
||||
var response = await Http.SendAsync(request, token).ConfigureAwait(false);
|
||||
using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
|
||||
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
Log.Info($"|PluginsManifest.{nameof(UpdateManifestAsync)}|Fetched plugins from manifest repo");
|
||||
|
||||
var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
|
||||
await using var json = await response.Content.ReadAsStreamAsync(token).ConfigureAwait(false);
|
||||
|
||||
UserPlugins = await JsonSerializer.DeserializeAsync<List<UserPlugin>>(json, cancellationToken: token).ConfigureAwait(false);
|
||||
|
||||
|
|
@ -56,4 +56,4 @@ namespace Flow.Launcher.Core.ExternalPlugins
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
namespace Flow.Launcher.Core.ExternalPlugins
|
||||
using System;
|
||||
|
||||
namespace Flow.Launcher.Core.ExternalPlugins
|
||||
{
|
||||
public record UserPlugin
|
||||
{
|
||||
|
|
@ -12,5 +14,8 @@
|
|||
public string UrlDownload { get; set; }
|
||||
public string UrlSourceCode { get; set; }
|
||||
public string IcoPath { get; set; }
|
||||
public DateTime LatestReleaseDate { get; set; }
|
||||
public DateTime DateAdded { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<UseWpf>true</UseWpf>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<OutputType>Library</OutputType>
|
||||
|
|
@ -53,9 +53,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Droplex" Version="1.4.1" />
|
||||
<PackageReference Include="FSharp.Core" Version="5.0.2" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.1.3" />
|
||||
<PackageReference Include="Droplex" Version="1.6.0" />
|
||||
<PackageReference Include="FSharp.Core" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.1" />
|
||||
<PackageReference Include="squirrel.windows" Version="1.5.2" NoWarn="NU1701" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
internal class ExecutablePlugin : JsonRPCPlugin
|
||||
{
|
||||
private readonly ProcessStartInfo _startInfo;
|
||||
public override string SupportedLanguage { get; set; } = AllowedLanguage.Executable;
|
||||
|
||||
public ExecutablePlugin(string filename)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -26,6 +27,8 @@ namespace Flow.Launcher.Core.Plugin
|
|||
public string Name { get; set; }
|
||||
public string Label { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string urlLabel { get; set; }
|
||||
public Uri url { get; set; }
|
||||
public bool Validation { get; set; }
|
||||
public List<string> Options { get; set; }
|
||||
public string DefaultValue { get; set; }
|
||||
|
|
@ -40,4 +43,4 @@ namespace Flow.Launcher.Core.Plugin
|
|||
DefaultValue = this.DefaultValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ using Control = System.Windows.Controls.Control;
|
|||
using Orientation = System.Windows.Controls.Orientation;
|
||||
using TextBox = System.Windows.Controls.TextBox;
|
||||
using UserControl = System.Windows.Controls.UserControl;
|
||||
using System.Windows.Documents;
|
||||
using static System.Windows.Forms.LinkLabel;
|
||||
using Droplex;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
|
|
@ -34,10 +38,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
protected PluginInitContext Context;
|
||||
public const string JsonRPC = "JsonRPC";
|
||||
|
||||
/// <summary>
|
||||
/// The language this JsonRPCPlugin support
|
||||
/// </summary>
|
||||
public abstract string SupportedLanguage { get; set; }
|
||||
protected abstract Task<Stream> RequestAsync(JsonRPCRequestModel rpcRequest, CancellationToken token = default);
|
||||
protected abstract string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default);
|
||||
|
||||
|
|
@ -256,9 +256,16 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
await using var registeredEvent = token.Register(() =>
|
||||
{
|
||||
if (!process.HasExited)
|
||||
process.Kill();
|
||||
sourceBuffer.Dispose();
|
||||
try
|
||||
{
|
||||
if (!process.HasExited)
|
||||
process.Kill();
|
||||
sourceBuffer.Dispose();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception("|JsonRPCPlugin.ExecuteAsync|Exception when kill process", e);
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
|
|
@ -285,7 +292,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
}
|
||||
|
||||
sourceBuffer.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
|
||||
return sourceBuffer;
|
||||
}
|
||||
|
||||
|
|
@ -332,9 +339,14 @@ namespace Flow.Launcher.Core.Plugin
|
|||
this.Context = context;
|
||||
await InitSettingAsync();
|
||||
}
|
||||
private static readonly Thickness settingControlMargin = new(10, 4, 10, 4);
|
||||
private static readonly Thickness settingPanelMargin = new(15, 20, 15, 20);
|
||||
private static readonly Thickness settingTextBlockMargin = new(10, 4, 10, 4);
|
||||
private static readonly Thickness settingControlMargin = new(0, 9, 18, 9);
|
||||
private static readonly Thickness settingCheckboxMargin = new(0, 9, 9, 9);
|
||||
private static readonly Thickness settingPanelMargin = new(0, 0, 0, 0);
|
||||
private static readonly Thickness settingTextBlockMargin = new(70, 9, 18, 9);
|
||||
private static readonly Thickness settingLabelPanelMargin = new(70, 9, 18, 9);
|
||||
private static readonly Thickness settingLabelMargin = new(0, 0, 0, 0);
|
||||
private static readonly Thickness settingDescMargin = new(0, 2, 0, 0);
|
||||
private static readonly Thickness settingSepMargin = new(0, 0, 0, 2);
|
||||
private JsonRpcConfigurationModel _settingsTemplate;
|
||||
|
||||
public Control CreateSettingPanel()
|
||||
|
|
@ -342,26 +354,63 @@ namespace Flow.Launcher.Core.Plugin
|
|||
if (Settings == null)
|
||||
return new();
|
||||
var settingWindow = new UserControl();
|
||||
var mainPanel = new StackPanel
|
||||
var mainPanel = new Grid
|
||||
{
|
||||
Margin = settingPanelMargin, Orientation = Orientation.Vertical
|
||||
Margin = settingPanelMargin, VerticalAlignment = VerticalAlignment.Center
|
||||
};
|
||||
settingWindow.Content = mainPanel;
|
||||
ColumnDefinition gridCol1 = new ColumnDefinition();
|
||||
ColumnDefinition gridCol2 = new ColumnDefinition();
|
||||
|
||||
gridCol1.Width = new GridLength(70, GridUnitType.Star);
|
||||
gridCol2.Width = new GridLength(30, GridUnitType.Star);
|
||||
mainPanel.ColumnDefinitions.Add(gridCol1);
|
||||
mainPanel.ColumnDefinitions.Add(gridCol2);
|
||||
settingWindow.Content = mainPanel;
|
||||
int rowCount = 0;
|
||||
foreach (var (type, attribute) in _settingsTemplate.Body)
|
||||
{
|
||||
Separator sep = new Separator();
|
||||
sep.VerticalAlignment = VerticalAlignment.Top;
|
||||
sep.Margin = settingSepMargin;
|
||||
sep.SetResourceReference(Separator.BackgroundProperty, "Color03B"); /* for theme change */
|
||||
var panel = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal, Margin = settingControlMargin
|
||||
Orientation = Orientation.Vertical,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = settingLabelPanelMargin
|
||||
};
|
||||
RowDefinition gridRow = new RowDefinition();
|
||||
mainPanel.RowDefinitions.Add(gridRow);
|
||||
var name = new TextBlock()
|
||||
{
|
||||
Text = attribute.Label,
|
||||
Width = 120,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = settingControlMargin,
|
||||
Margin = settingLabelMargin,
|
||||
TextWrapping = TextWrapping.WrapWithOverflow
|
||||
};
|
||||
var desc = new TextBlock()
|
||||
{
|
||||
Text = attribute.Description,
|
||||
FontSize = 12,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
Margin = settingDescMargin,
|
||||
TextWrapping = TextWrapping.WrapWithOverflow
|
||||
};
|
||||
desc.SetResourceReference(TextBlock.ForegroundProperty, "Color04B");
|
||||
|
||||
if (attribute.Description == null) /* if no description, hide */
|
||||
desc.Visibility = Visibility.Collapsed;
|
||||
|
||||
|
||||
if (type != "textBlock") /* if textBlock, hide desc */
|
||||
{
|
||||
panel.Children.Add(name);
|
||||
panel.Children.Add(desc);
|
||||
}
|
||||
|
||||
|
||||
Grid.SetColumn(panel, 0);
|
||||
Grid.SetRow(panel, rowCount);
|
||||
|
||||
FrameworkElement contentControl;
|
||||
|
||||
|
|
@ -373,18 +422,28 @@ namespace Flow.Launcher.Core.Plugin
|
|||
{
|
||||
Text = attribute.Description.Replace("\\r\\n", "\r\n"),
|
||||
Margin = settingTextBlockMargin,
|
||||
MaxWidth = 500,
|
||||
TextWrapping = TextWrapping.WrapWithOverflow
|
||||
Padding = new Thickness(0, 0, 0, 0),
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
|
||||
TextAlignment = TextAlignment.Left,
|
||||
TextWrapping = TextWrapping.Wrap
|
||||
};
|
||||
Grid.SetColumn(contentControl, 0);
|
||||
Grid.SetColumnSpan(contentControl, 2);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
}
|
||||
case "input":
|
||||
{
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
Width = 300,
|
||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
||||
Margin = settingControlMargin,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
textBox.TextChanged += (_, _) =>
|
||||
|
|
@ -392,17 +451,59 @@ namespace Flow.Launcher.Core.Plugin
|
|||
Settings[attribute.Name] = textBox.Text;
|
||||
};
|
||||
contentControl = textBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
}
|
||||
case "inputWithFileBtn":
|
||||
{
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
Margin = new Thickness(10, 0, 0, 0),
|
||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
textBox.TextChanged += (_, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = textBox.Text;
|
||||
};
|
||||
var Btn = new System.Windows.Controls.Button()
|
||||
{
|
||||
Margin = new Thickness(10, 0, 0, 0), Content = "Browse"
|
||||
};
|
||||
var dockPanel = new DockPanel()
|
||||
{
|
||||
Margin = settingControlMargin
|
||||
};
|
||||
DockPanel.SetDock(Btn, Dock.Right);
|
||||
dockPanel.Children.Add(Btn);
|
||||
dockPanel.Children.Add(textBox);
|
||||
contentControl = dockPanel;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
}
|
||||
case "textarea":
|
||||
{
|
||||
var textBox = new TextBox()
|
||||
{
|
||||
Width = 300,
|
||||
Height = 120,
|
||||
Margin = settingControlMargin,
|
||||
VerticalAlignment = VerticalAlignment.Center,
|
||||
TextWrapping = TextWrapping.WrapWithOverflow,
|
||||
AcceptsReturn = true,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
Text = Settings[attribute.Name] as string ?? string.Empty,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
|
|
@ -411,16 +512,23 @@ namespace Flow.Launcher.Core.Plugin
|
|||
Settings[attribute.Name] = ((TextBox)sender).Text;
|
||||
};
|
||||
contentControl = textBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
}
|
||||
case "passwordBox":
|
||||
{
|
||||
var passwordBox = new PasswordBox()
|
||||
{
|
||||
Width = 300,
|
||||
Margin = settingControlMargin,
|
||||
Password = Settings[attribute.Name] as string ?? string.Empty,
|
||||
PasswordChar = attribute.passwordChar == default ? '*' : attribute.passwordChar,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
passwordBox.PasswordChanged += (sender, _) =>
|
||||
|
|
@ -428,29 +536,45 @@ namespace Flow.Launcher.Core.Plugin
|
|||
Settings[attribute.Name] = ((PasswordBox)sender).Password;
|
||||
};
|
||||
contentControl = passwordBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
}
|
||||
case "dropdown":
|
||||
{
|
||||
var comboBox = new ComboBox()
|
||||
var comboBox = new System.Windows.Controls.ComboBox()
|
||||
{
|
||||
ItemsSource = attribute.Options,
|
||||
SelectedItem = Settings[attribute.Name],
|
||||
Margin = settingControlMargin,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
comboBox.SelectionChanged += (sender, _) =>
|
||||
{
|
||||
Settings[attribute.Name] = (string)((ComboBox)sender).SelectedItem;
|
||||
Settings[attribute.Name] = (string)((System.Windows.Controls.ComboBox)sender).SelectedItem;
|
||||
};
|
||||
contentControl = comboBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
}
|
||||
case "checkbox":
|
||||
var checkBox = new CheckBox
|
||||
{
|
||||
IsChecked = Settings[attribute.Name] is bool isChecked ? isChecked : bool.Parse(attribute.DefaultValue),
|
||||
Margin = settingControlMargin,
|
||||
Margin = settingCheckboxMargin,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
|
||||
ToolTip = attribute.Description
|
||||
};
|
||||
checkBox.Click += (sender, _) =>
|
||||
|
|
@ -458,15 +582,43 @@ namespace Flow.Launcher.Core.Plugin
|
|||
Settings[attribute.Name] = ((CheckBox)sender).IsChecked;
|
||||
};
|
||||
contentControl = checkBox;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
case "hyperlink":
|
||||
var hyperlink = new Hyperlink
|
||||
{
|
||||
ToolTip = attribute.Description, NavigateUri = attribute.url
|
||||
};
|
||||
var linkbtn = new System.Windows.Controls.Button
|
||||
{
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Right, Margin = settingControlMargin
|
||||
};
|
||||
linkbtn.Content = attribute.urlLabel;
|
||||
|
||||
contentControl = linkbtn;
|
||||
Grid.SetColumn(contentControl, 1);
|
||||
Grid.SetRow(contentControl, rowCount);
|
||||
if (rowCount != 0)
|
||||
mainPanel.Children.Add(sep);
|
||||
Grid.SetRow(sep, rowCount);
|
||||
Grid.SetColumn(sep, 0);
|
||||
Grid.SetColumnSpan(sep, 2);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (type != "textBlock")
|
||||
_settingControls[attribute.Name] = contentControl;
|
||||
panel.Children.Add(name);
|
||||
panel.Children.Add(contentControl);
|
||||
mainPanel.Children.Add(panel);
|
||||
mainPanel.Children.Add(contentControl);
|
||||
rowCount++;
|
||||
|
||||
}
|
||||
return settingWindow;
|
||||
}
|
||||
|
|
@ -502,7 +654,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
case PasswordBox passwordBox:
|
||||
passwordBox.Dispatcher.Invoke(() => passwordBox.Password = value as string);
|
||||
break;
|
||||
case ComboBox comboBox:
|
||||
case System.Windows.Controls.ComboBox comboBox:
|
||||
comboBox.Dispatcher.Invoke(() => comboBox.SelectedItem = value);
|
||||
break;
|
||||
case CheckBox checkBox:
|
||||
|
|
|
|||
53
Flow.Launcher.Core/Plugin/NodePlugin.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution of JavaScript & TypeScript plugins
|
||||
/// </summary>
|
||||
internal class NodePlugin : JsonRPCPlugin
|
||||
{
|
||||
private readonly ProcessStartInfo _startInfo;
|
||||
|
||||
public NodePlugin(string filename)
|
||||
{
|
||||
_startInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = filename,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true
|
||||
};
|
||||
}
|
||||
|
||||
protected override Task<Stream> RequestAsync(JsonRPCRequestModel request, CancellationToken token = default)
|
||||
{
|
||||
_startInfo.ArgumentList[1] = request.ToString();
|
||||
return ExecuteAsync(_startInfo, token);
|
||||
}
|
||||
|
||||
protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default)
|
||||
{
|
||||
// since this is not static, request strings will build up in ArgumentList if index is not specified
|
||||
_startInfo.ArgumentList[1] = rpcRequest.ToString();
|
||||
return Execute(_startInfo);
|
||||
}
|
||||
|
||||
public override async Task InitAsync(PluginInitContext context)
|
||||
{
|
||||
_startInfo.ArgumentList.Add(context.CurrentPluginMetadata.ExecuteFilePath);
|
||||
_startInfo.ArgumentList.Add(string.Empty);
|
||||
await base.InitAsync(context);
|
||||
_startInfo.WorkingDirectory = context.CurrentPluginMetadata.PluginDirectory;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -166,18 +166,18 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
public static ICollection<PluginPair> ValidPluginsForQuery(Query query)
|
||||
{
|
||||
if (NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||
{
|
||||
var plugin = NonGlobalPlugins[query.ActionKeyword];
|
||||
return new List<PluginPair>
|
||||
{
|
||||
plugin
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
if (query is null)
|
||||
return Array.Empty<PluginPair>();
|
||||
|
||||
if (!NonGlobalPlugins.ContainsKey(query.ActionKeyword))
|
||||
return GlobalPlugins;
|
||||
}
|
||||
|
||||
|
||||
var plugin = NonGlobalPlugins[query.ActionKeyword];
|
||||
return new List<PluginPair>
|
||||
{
|
||||
plugin
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<List<Result>> QueryForPluginAsync(PluginPair pair, Query query, CancellationToken token)
|
||||
|
|
|
|||
|
|
@ -1,30 +1,38 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Droplex;
|
||||
using Flow.Launcher.Infrastructure;
|
||||
using Flow.Launcher.Core.ExternalPlugins.Environments;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using Stopwatch = Flow.Launcher.Infrastructure.Stopwatch;
|
||||
|
||||
namespace Flow.Launcher.Core.Plugin
|
||||
{
|
||||
public static class PluginsLoader
|
||||
{
|
||||
public const string PythonExecutable = "pythonw.exe";
|
||||
|
||||
public static List<PluginPair> Plugins(List<PluginMetadata> metadatas, PluginsSettings settings)
|
||||
{
|
||||
var dotnetPlugins = DotNetPlugins(metadatas);
|
||||
var pythonPlugins = PythonPlugins(metadatas, settings);
|
||||
|
||||
var pythonEnv = new PythonEnvironment(metadatas, settings);
|
||||
var tsEnv = new TypeScriptEnvironment(metadatas, settings);
|
||||
var jsEnv = new JavaScriptEnvironment(metadatas, settings);
|
||||
var pythonPlugins = pythonEnv.Setup();
|
||||
var tsPlugins = tsEnv.Setup();
|
||||
var jsPlugins = jsEnv.Setup();
|
||||
|
||||
var executablePlugins = ExecutablePlugins(metadatas);
|
||||
var plugins = dotnetPlugins.Concat(pythonPlugins).Concat(executablePlugins).ToList();
|
||||
|
||||
var plugins = dotnetPlugins
|
||||
.Concat(pythonPlugins)
|
||||
.Concat(tsPlugins)
|
||||
.Concat(jsPlugins)
|
||||
.Concat(executablePlugins)
|
||||
.ToList();
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
|
@ -83,10 +91,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return;
|
||||
}
|
||||
|
||||
plugins.Add(new PluginPair
|
||||
{
|
||||
Plugin = plugin, Metadata = metadata
|
||||
});
|
||||
plugins.Add(new PluginPair {Plugin = plugin, Metadata = metadata});
|
||||
});
|
||||
metadata.InitTime += milliseconds;
|
||||
}
|
||||
|
|
@ -111,102 +116,10 @@ namespace Flow.Launcher.Core.Plugin
|
|||
return plugins;
|
||||
}
|
||||
|
||||
public static IEnumerable<PluginPair> PythonPlugins(List<PluginMetadata> source, PluginsSettings settings)
|
||||
{
|
||||
if (!source.Any(o => o.Language.ToUpper() is AllowedLanguage.Python or AllowedLanguage.PythonV2))
|
||||
return new List<PluginPair>();
|
||||
|
||||
if (!string.IsNullOrEmpty(settings.PythonDirectory) && FilesFolders.LocationExists(settings.PythonDirectory))
|
||||
return SetPythonPathForPluginPairs(source, Path.Combine(settings.PythonDirectory, PythonExecutable));
|
||||
|
||||
var pythonPath = string.Empty;
|
||||
|
||||
if (MessageBox.Show("Flow detected you have installed Python plugins, which " +
|
||||
"will need Python to run. Would you like to download Python? " +
|
||||
Environment.NewLine + Environment.NewLine +
|
||||
"Click no if it's already installed, " +
|
||||
"and you will be prompted to select the folder that contains the Python executable",
|
||||
string.Empty, MessageBoxButtons.YesNo) == DialogResult.No
|
||||
&& string.IsNullOrEmpty(settings.PythonDirectory))
|
||||
{
|
||||
var dlg = new FolderBrowserDialog
|
||||
{
|
||||
SelectedPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles)
|
||||
};
|
||||
|
||||
var result = dlg.ShowDialog();
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
string pythonDirectory = dlg.SelectedPath;
|
||||
if (!string.IsNullOrEmpty(pythonDirectory))
|
||||
{
|
||||
pythonPath = Path.Combine(pythonDirectory, PythonExecutable);
|
||||
if (File.Exists(pythonPath))
|
||||
{
|
||||
settings.PythonDirectory = pythonDirectory;
|
||||
Constant.PythonPath = pythonPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("Can't find python in given directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var installedPythonDirectory = Path.Combine(DataLocation.DataDirectory(), "PythonEmbeddable");
|
||||
|
||||
// Python 3.8.9 is used for Windows 7 compatibility
|
||||
DroplexPackage.Drop(App.python_3_8_9_embeddable, installedPythonDirectory).Wait();
|
||||
|
||||
pythonPath = Path.Combine(installedPythonDirectory, PythonExecutable);
|
||||
if (FilesFolders.FileExists(pythonPath))
|
||||
{
|
||||
settings.PythonDirectory = installedPythonDirectory;
|
||||
Constant.PythonPath = pythonPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error("PluginsLoader",
|
||||
$"Failed to set Python path after Droplex install, {pythonPath} does not exist",
|
||||
"PythonPlugins");
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(settings.PythonDirectory) || string.IsNullOrEmpty(pythonPath))
|
||||
{
|
||||
MessageBox.Show(
|
||||
"Unable to set Python executable path, please try from Flow's settings (scroll down to the bottom).");
|
||||
Log.Error("PluginsLoader",
|
||||
$"Not able to successfully set Python path, the PythonDirectory variable is still an empty string.",
|
||||
"PythonPlugins");
|
||||
|
||||
return new List<PluginPair>();
|
||||
}
|
||||
|
||||
return SetPythonPathForPluginPairs(source, pythonPath);
|
||||
}
|
||||
|
||||
private static IEnumerable<PluginPair> SetPythonPathForPluginPairs(IEnumerable<PluginMetadata> source, string pythonPath)
|
||||
=> source
|
||||
.Where(o => o.Language.ToUpper() is AllowedLanguage.Python or AllowedLanguage.PythonV2)
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = metadata.Language.ToUpper() switch
|
||||
{
|
||||
AllowedLanguage.Python => new PythonPlugin(pythonPath),
|
||||
AllowedLanguage.PythonV2 => new PythonPluginV2(pythonPath),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
},
|
||||
Metadata = metadata
|
||||
})
|
||||
.ToList();
|
||||
|
||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
public static IEnumerable<PluginPair> ExecutablePlugins(IEnumerable<PluginMetadata> source)
|
||||
{
|
||||
return source
|
||||
.Where(o => o.Language.ToUpper() == AllowedLanguage.Executable)
|
||||
.Where(o => o.Language.Equals(AllowedLanguage.Executable, StringComparison.OrdinalIgnoreCase))
|
||||
.Select(metadata => new PluginPair
|
||||
{
|
||||
Plugin = new ExecutablePlugin(metadata.ExecuteFilePath), Metadata = metadata
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
|
@ -11,7 +11,6 @@ namespace Flow.Launcher.Core.Plugin
|
|||
internal class PythonPlugin : JsonRPCPlugin
|
||||
{
|
||||
private readonly ProcessStartInfo _startInfo;
|
||||
public override string SupportedLanguage { get; set; } = AllowedLanguage.Python;
|
||||
|
||||
public PythonPlugin(string filename)
|
||||
{
|
||||
|
|
@ -46,6 +45,7 @@ namespace Flow.Launcher.Core.Plugin
|
|||
|
||||
protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default)
|
||||
{
|
||||
// since this is not static, request strings will build up in ArgumentList if index is not specified
|
||||
_startInfo.ArgumentList[2] = rpcRequest.ToString();
|
||||
_startInfo.WorkingDirectory = Context.CurrentPluginMetadata.PluginDirectory;
|
||||
// TODO: Async Action
|
||||
|
|
|
|||
|
|
@ -96,9 +96,16 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
LoadLanguage(language);
|
||||
}
|
||||
Settings.Language = language.LanguageCode;
|
||||
CultureInfo.CurrentCulture = new CultureInfo(language.LanguageCode);
|
||||
// Culture of this thread
|
||||
// Use CreateSpecificCulture to preserve possible user-override settings in Windows
|
||||
CultureInfo.CurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode);
|
||||
CultureInfo.CurrentUICulture = CultureInfo.CurrentCulture;
|
||||
// App domain
|
||||
CultureInfo.DefaultThreadCurrentCulture = CultureInfo.CreateSpecificCulture(language.LanguageCode);
|
||||
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.DefaultThreadCurrentCulture;
|
||||
|
||||
// Raise event after culture is set
|
||||
Settings.Language = language.LanguageCode;
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
UpdatePluginMetadataTranslations();
|
||||
|
|
@ -115,7 +122,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
if (languageToSet != AvailableLanguages.Chinese && languageToSet != AvailableLanguages.Chinese_TW)
|
||||
return false;
|
||||
|
||||
if (MessageBox.Show("Do you want to turn on search with Pinyin?", string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
// No other languages should show the following text so just make it hard-coded
|
||||
// "Do you want to search with pinyin?"
|
||||
string text = languageToSet == AvailableLanguages.Chinese ? "是否启用拼音搜索?" : "是否啓用拼音搜索?" ;
|
||||
|
||||
if (MessageBox.Show(text, string.Empty, MessageBoxButton.YesNo) == MessageBoxResult.No)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
|
@ -182,7 +193,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
p.Metadata.Name = pluginI18N.GetTranslatedPluginTitle();
|
||||
p.Metadata.Description = pluginI18N.GetTranslatedPluginDescription();
|
||||
pluginI18N.OnCultureInfoChanged(CultureInfo.CurrentCulture);
|
||||
pluginI18N.OnCultureInfoChanged(CultureInfo.DefaultThreadCurrentCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
@ -221,4 +232,4 @@ namespace Flow.Launcher.Core.Resource
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using System.Globalization;
|
|||
using System.Reflection;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Flow.Launcher.Core
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
public class LocalizationConverter : IValueConverter
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
using System.ComponentModel;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
|
||||
namespace Flow.Launcher.Core
|
||||
namespace Flow.Launcher.Core.Resource
|
||||
{
|
||||
public class LocalizedDescriptionAttribute : DescriptionAttribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
public class Theme
|
||||
{
|
||||
private const int ShadowExtraMargin = 12;
|
||||
private const int ShadowExtraMargin = 32;
|
||||
|
||||
private readonly List<string> _themeDirectories = new List<string>();
|
||||
private ResourceDictionary _oldResource;
|
||||
|
|
@ -36,7 +36,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
_themeDirectories.Add(DirectoryPath);
|
||||
_themeDirectories.Add(UserDirectoryPath);
|
||||
MakesureThemeDirectoriesExist();
|
||||
MakeSureThemeDirectoriesExist();
|
||||
|
||||
var dicts = Application.Current.Resources.MergedDictionaries;
|
||||
_oldResource = dicts.First(d =>
|
||||
|
|
@ -55,20 +55,17 @@ namespace Flow.Launcher.Core.Resource
|
|||
_oldTheme = Path.GetFileNameWithoutExtension(_oldResource.Source.AbsolutePath);
|
||||
}
|
||||
|
||||
private void MakesureThemeDirectoriesExist()
|
||||
private void MakeSureThemeDirectoriesExist()
|
||||
{
|
||||
foreach (string dir in _themeDirectories)
|
||||
foreach (var dir in _themeDirectories.Where(dir => !Directory.Exists(dir)))
|
||||
{
|
||||
if (!Directory.Exists(dir))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|Theme.MakesureThemeDirectoriesExist|Exception when create directory <{dir}>", e);
|
||||
}
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|Theme.MakesureThemeDirectoriesExist|Exception when create directory <{dir}>", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -82,13 +79,14 @@ namespace Flow.Launcher.Core.Resource
|
|||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new DirectoryNotFoundException("Theme path can't be found <{path}>");
|
||||
|
||||
Settings.Theme = theme;
|
||||
|
||||
|
||||
// reload all resources even if the theme itself hasn't changed in order to pickup changes
|
||||
// to things like fonts
|
||||
UpdateResourceDictionary(GetResourceDictionary());
|
||||
UpdateResourceDictionary(GetResourceDictionary(theme));
|
||||
|
||||
Settings.Theme = theme;
|
||||
|
||||
|
||||
//always allow re-loading default theme, in case of failure of switching to a new theme from default theme
|
||||
if (_oldTheme != theme || theme == defaultTheme)
|
||||
{
|
||||
|
|
@ -134,9 +132,9 @@ namespace Flow.Launcher.Core.Resource
|
|||
_oldResource = dictionaryToUpdate;
|
||||
}
|
||||
|
||||
private ResourceDictionary CurrentThemeResourceDictionary()
|
||||
private ResourceDictionary GetThemeResourceDictionary(string theme)
|
||||
{
|
||||
var uri = GetThemePath(Settings.Theme);
|
||||
var uri = GetThemePath(theme);
|
||||
var dict = new ResourceDictionary
|
||||
{
|
||||
Source = new Uri(uri, UriKind.Absolute)
|
||||
|
|
@ -145,10 +143,12 @@ namespace Flow.Launcher.Core.Resource
|
|||
return dict;
|
||||
}
|
||||
|
||||
public ResourceDictionary GetResourceDictionary()
|
||||
private ResourceDictionary CurrentThemeResourceDictionary() => GetThemeResourceDictionary(Settings.Theme);
|
||||
|
||||
public ResourceDictionary GetResourceDictionary(string theme)
|
||||
{
|
||||
var dict = CurrentThemeResourceDictionary();
|
||||
|
||||
var dict = GetThemeResourceDictionary(theme);
|
||||
|
||||
if (dict["QueryBoxStyle"] is Style queryBoxStyle &&
|
||||
dict["QuerySuggestionBoxStyle"] is Style querySuggestionBoxStyle)
|
||||
{
|
||||
|
|
@ -200,6 +200,11 @@ namespace Flow.Launcher.Core.Resource
|
|||
return dict;
|
||||
}
|
||||
|
||||
private ResourceDictionary GetCurrentResourceDictionary( )
|
||||
{
|
||||
return GetResourceDictionary(Settings.Theme);
|
||||
}
|
||||
|
||||
public List<string> LoadAvailableThemes()
|
||||
{
|
||||
List<string> themes = new List<string>();
|
||||
|
|
@ -229,7 +234,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
public void AddDropShadowEffectToCurrentTheme()
|
||||
{
|
||||
var dict = GetResourceDictionary();
|
||||
var dict = GetCurrentResourceDictionary();
|
||||
|
||||
var windowBorderStyle = dict["WindowBorderStyle"] as Style;
|
||||
|
||||
|
|
@ -238,9 +243,10 @@ namespace Flow.Launcher.Core.Resource
|
|||
Property = Border.EffectProperty,
|
||||
Value = new DropShadowEffect
|
||||
{
|
||||
Opacity = 0.4,
|
||||
ShadowDepth = 2,
|
||||
BlurRadius = 15
|
||||
Opacity = 0.3,
|
||||
ShadowDepth = 12,
|
||||
Direction = 270,
|
||||
BlurRadius = 30
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -250,7 +256,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
marginSetter = new Setter()
|
||||
{
|
||||
Property = Border.MarginProperty,
|
||||
Value = new Thickness(ShadowExtraMargin),
|
||||
Value = new Thickness(ShadowExtraMargin, 12, ShadowExtraMargin, ShadowExtraMargin),
|
||||
};
|
||||
windowBorderStyle.Setters.Add(marginSetter);
|
||||
}
|
||||
|
|
@ -272,7 +278,7 @@ namespace Flow.Launcher.Core.Resource
|
|||
|
||||
public void RemoveDropShadowEffectFromCurrentTheme()
|
||||
{
|
||||
var dict = CurrentThemeResourceDictionary();
|
||||
var dict = GetCurrentResourceDictionary();
|
||||
var windowBorderStyle = dict["WindowBorderStyle"] as Style;
|
||||
|
||||
var effectSetter = windowBorderStyle.Setters.FirstOrDefault(setterBase => setterBase is Setter setter && setter.Property == Border.EffectProperty) as Setter;
|
||||
|
|
|
|||
19
Flow.Launcher.Core/Resource/TranslationConverter.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ namespace Flow.Launcher.Core
|
|||
|
||||
public async Task UpdateAppAsync(IPublicAPI api, bool silentUpdate = true)
|
||||
{
|
||||
await UpdateLock.WaitAsync();
|
||||
await UpdateLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (!silentUpdate)
|
||||
|
|
@ -79,7 +79,7 @@ namespace Flow.Launcher.Core
|
|||
await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var newVersionTips = NewVersinoTips(newReleaseVersion.ToString());
|
||||
var newVersionTips = NewVersionTips(newReleaseVersion.ToString());
|
||||
|
||||
Log.Info($"|Updater.UpdateApp|Update success:{newVersionTips}");
|
||||
|
||||
|
|
@ -88,9 +88,13 @@ namespace Flow.Launcher.Core
|
|||
UpdateManager.RestartApp(Constant.ApplicationFileName);
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
|
||||
catch (Exception e)
|
||||
{
|
||||
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))
|
||||
Log.Exception($"|Updater.UpdateApp|Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
|
||||
else
|
||||
Log.Exception($"|Updater.UpdateApp|Error Occurred", e);
|
||||
|
||||
if (!silentUpdate)
|
||||
api.ShowMsg(api.GetTranslation("update_flowlauncher_fail"),
|
||||
api.GetTranslation("update_flowlauncher_check_connection"));
|
||||
|
|
@ -114,7 +118,7 @@ namespace Flow.Launcher.Core
|
|||
public string HtmlUrl { get; [UsedImplicitly] set; }
|
||||
}
|
||||
|
||||
/// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs
|
||||
// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs
|
||||
private async Task<UpdateManager> GitHubUpdateManagerAsync(string repository)
|
||||
{
|
||||
var uri = new Uri(repository);
|
||||
|
|
@ -137,12 +141,12 @@ namespace Flow.Launcher.Core
|
|||
return manager;
|
||||
}
|
||||
|
||||
public string NewVersinoTips(string version)
|
||||
public string NewVersionTips(string version)
|
||||
{
|
||||
var translater = InternationalizationManager.Instance;
|
||||
var tips = string.Format(translater.GetTranslation("newVersionTips"), version);
|
||||
var translator = InternationalizationManager.Instance;
|
||||
var tips = string.Format(translator.GetTranslation("newVersionTips"), version);
|
||||
|
||||
return tips;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
|
|
@ -21,6 +21,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
public static readonly string PreinstalledDirectory = Path.Combine(ProgramDirectory, Plugins);
|
||||
public const string Issue = "https://github.com/Flow-Launcher/Flow.Launcher/issues/new";
|
||||
public static readonly string Version = FileVersionInfo.GetVersionInfo(Assembly.Location.NonNull()).ProductVersion;
|
||||
public static readonly string Dev = "Dev";
|
||||
public const string Documentation = "https://flowlauncher.com/docs/#/usage-tips";
|
||||
|
||||
public static readonly int ThumbnailSize = 64;
|
||||
|
|
@ -28,8 +29,10 @@ namespace Flow.Launcher.Infrastructure
|
|||
public static readonly string DefaultIcon = Path.Combine(ImagesDirectory, "app.png");
|
||||
public static readonly string ErrorIcon = Path.Combine(ImagesDirectory, "app_error.png");
|
||||
public static readonly string MissingImgIcon = Path.Combine(ImagesDirectory, "app_missing_img.png");
|
||||
public static readonly string LoadingImgIcon = Path.Combine(ImagesDirectory, "loading.png");
|
||||
|
||||
public static string PythonPath;
|
||||
public static string NodePath;
|
||||
|
||||
public static readonly string QueryTextBoxIconImagePath = $"{ProgramDirectory}\\Images\\mainsearch.svg";
|
||||
|
||||
|
|
@ -44,6 +47,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
public const string Logs = "Logs";
|
||||
|
||||
public const string Website = "https://flowlauncher.com";
|
||||
public const string SponsorPage = "https://github.com/sponsors/Flow-Launcher";
|
||||
public const string GitHub = "https://github.com/Flow-Launcher/Flow.Launcher";
|
||||
public const string Docs = "https://flowlauncher.com/docs";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ namespace Flow.Launcher.Infrastructure.Exception
|
|||
sb.AppendLine($"* IntPtr Length: {IntPtr.Size}");
|
||||
sb.AppendLine($"* x64: {Environment.Is64BitOperatingSystem}");
|
||||
sb.AppendLine($"* Python Path: {Constant.PythonPath}");
|
||||
sb.AppendLine($"* Node Path: {Constant.NodePath}");
|
||||
sb.AppendLine($"* CLR Version: {Environment.Version}");
|
||||
sb.AppendLine($"* Installed .NET Framework: ");
|
||||
foreach (var result in GetFrameworkVersionFromRegistry())
|
||||
|
|
|
|||
89
Flow.Launcher.Infrastructure/FileExplorerHelper.cs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure
|
||||
{
|
||||
public static class FileExplorerHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the path of the file explorer that is currently in the foreground
|
||||
/// </summary>
|
||||
public static string GetActiveExplorerPath()
|
||||
{
|
||||
var explorerWindow = GetActiveExplorer();
|
||||
string locationUrl = explorerWindow?.LocationURL;
|
||||
return !string.IsNullOrEmpty(locationUrl) ? new Uri(locationUrl).LocalPath + "\\" : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file explorer that is currently in the foreground
|
||||
/// </summary>
|
||||
private static dynamic GetActiveExplorer()
|
||||
{
|
||||
Type type = Type.GetTypeFromProgID("Shell.Application");
|
||||
if (type == null) return null;
|
||||
dynamic shell = Activator.CreateInstance(type);
|
||||
if (shell == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var explorerWindows = new List<dynamic>();
|
||||
var openWindows = shell.Windows();
|
||||
for (int i = 0; i < openWindows.Count; i++)
|
||||
{
|
||||
var window = openWindows.Item(i);
|
||||
if (window == null) continue;
|
||||
|
||||
// find the desired window and make sure that it is indeed a file explorer
|
||||
// we don't want the Internet Explorer or the classic control panel
|
||||
// ToLower() is needed, because Windows can report the path as "C:\\Windows\\Explorer.EXE"
|
||||
if (Path.GetFileName((string)window.FullName)?.ToLower() == "explorer.exe")
|
||||
{
|
||||
explorerWindows.Add(window);
|
||||
}
|
||||
}
|
||||
|
||||
if (explorerWindows.Count == 0) return null;
|
||||
|
||||
var zOrders = GetZOrder(explorerWindows);
|
||||
|
||||
return explorerWindows.Zip(zOrders).MinBy(x => x.Second).First;
|
||||
}
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||
|
||||
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the z-order for one or more windows atomically with respect to each other. In Windows, smaller z-order is higher. If the window is not top level, the z order is returned as -1.
|
||||
/// </summary>
|
||||
private static IEnumerable<int> GetZOrder(List<dynamic> hWnds)
|
||||
{
|
||||
var z = new int[hWnds.Count];
|
||||
for (var i = 0; i < hWnds.Count; i++) z[i] = -1;
|
||||
|
||||
var index = 0;
|
||||
var numRemaining = hWnds.Count;
|
||||
EnumWindows((wnd, _) =>
|
||||
{
|
||||
var searchIndex = hWnds.FindIndex(x => x.HWND == wnd.ToInt32());
|
||||
if (searchIndex != -1)
|
||||
{
|
||||
z[searchIndex] = index;
|
||||
numRemaining--;
|
||||
if (numRemaining == 0) return false;
|
||||
}
|
||||
index++;
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
|
||||
return z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<ProjectGuid>{4FD29318-A8AB-4D8F-AA47-60BC241B8DA3}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<UseWpf>true</UseWpf>
|
||||
|
|
@ -53,27 +53,16 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="16.10.56" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Threading" Version="17.4.27" />
|
||||
<PackageReference Include="NLog" Version="4.7.10" />
|
||||
<PackageReference Include="NLog.Schema" Version="4.7.10" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="4.13.0" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="5.0.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-->
|
||||
<PackageReference Include="ToolGood.Words.Pinyin" Version="3.0.1.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="pinyindb\pinyin_gwoyeu_mapping.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="pinyindb\pinyin_mapping.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="pinyindb\unicode_to_hanyu_pinyin.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
|
|
@ -16,7 +18,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
/// <summary>
|
||||
/// http://www.yinwang.org/blog-cn/2015/11/21/programming-philosophy
|
||||
/// </summary>
|
||||
public static T NonNull<T>(this T obj)
|
||||
public static T NonNull<T>(this T? obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Windows.Input;
|
||||
|
||||
|
|
@ -11,10 +12,10 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
public bool Shift { get; set; }
|
||||
public bool Win { get; set; }
|
||||
public bool Ctrl { get; set; }
|
||||
public Key CharKey { get; set; }
|
||||
|
||||
public Key CharKey { get; set; } = Key.None;
|
||||
|
||||
Dictionary<Key, string> specialSymbolDictionary = new Dictionary<Key, string>
|
||||
private static readonly Dictionary<Key, string> specialSymbolDictionary = new Dictionary<Key, string>
|
||||
{
|
||||
{Key.Space, "Space"},
|
||||
{Key.Oem3, "~"}
|
||||
|
|
@ -27,19 +28,19 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
ModifierKeys modifierKeys = ModifierKeys.None;
|
||||
if (Alt)
|
||||
{
|
||||
modifierKeys = ModifierKeys.Alt;
|
||||
modifierKeys |= ModifierKeys.Alt;
|
||||
}
|
||||
if (Shift)
|
||||
{
|
||||
modifierKeys = modifierKeys | ModifierKeys.Shift;
|
||||
modifierKeys |= ModifierKeys.Shift;
|
||||
}
|
||||
if (Win)
|
||||
{
|
||||
modifierKeys = modifierKeys | ModifierKeys.Windows;
|
||||
modifierKeys |= ModifierKeys.Windows;
|
||||
}
|
||||
if (Ctrl)
|
||||
{
|
||||
modifierKeys = modifierKeys | ModifierKeys.Control;
|
||||
modifierKeys |= ModifierKeys.Control;
|
||||
}
|
||||
return modifierKeys;
|
||||
}
|
||||
|
|
@ -86,7 +87,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
Ctrl = true;
|
||||
keys.Remove("Ctrl");
|
||||
}
|
||||
if (keys.Count > 0)
|
||||
if (keys.Count == 1)
|
||||
{
|
||||
string charKey = keys[0];
|
||||
KeyValuePair<Key, string>? specialSymbolPair = specialSymbolDictionary.FirstOrDefault(pair => pair.Value == charKey);
|
||||
|
|
@ -98,7 +99,7 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
{
|
||||
try
|
||||
{
|
||||
CharKey = (Key) Enum.Parse(typeof (Key), charKey);
|
||||
CharKey = (Key)Enum.Parse(typeof(Key), charKey);
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
|
|
@ -110,36 +111,116 @@ namespace Flow.Launcher.Infrastructure.Hotkey
|
|||
|
||||
public override string ToString()
|
||||
{
|
||||
string text = string.Empty;
|
||||
List<string> keys = new List<string>();
|
||||
if (Ctrl)
|
||||
{
|
||||
text += "Ctrl + ";
|
||||
keys.Add("Ctrl");
|
||||
}
|
||||
if (Alt)
|
||||
{
|
||||
text += "Alt + ";
|
||||
keys.Add("Alt");
|
||||
}
|
||||
if (Shift)
|
||||
{
|
||||
text += "Shift + ";
|
||||
keys.Add("Shift");
|
||||
}
|
||||
if (Win)
|
||||
{
|
||||
text += "Win + ";
|
||||
keys.Add("Win");
|
||||
}
|
||||
|
||||
if (CharKey != Key.None)
|
||||
{
|
||||
text += specialSymbolDictionary.ContainsKey(CharKey)
|
||||
keys.Add(specialSymbolDictionary.ContainsKey(CharKey)
|
||||
? specialSymbolDictionary[CharKey]
|
||||
: CharKey.ToString();
|
||||
: CharKey.ToString());
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(text))
|
||||
return string.Join(" + ", keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate hotkey
|
||||
/// </summary>
|
||||
/// <param name="validateKeyGestrue">Try to validate hotkey as a KeyGesture.</param>
|
||||
/// <returns></returns>
|
||||
public bool Validate(bool validateKeyGestrue = false)
|
||||
{
|
||||
switch (CharKey)
|
||||
{
|
||||
text = text.Remove(text.Length - 3);
|
||||
case Key.LeftAlt:
|
||||
case Key.RightAlt:
|
||||
case Key.LeftCtrl:
|
||||
case Key.RightCtrl:
|
||||
case Key.LeftShift:
|
||||
case Key.RightShift:
|
||||
case Key.LWin:
|
||||
case Key.RWin:
|
||||
case Key.None:
|
||||
return false;
|
||||
default:
|
||||
if (validateKeyGestrue)
|
||||
{
|
||||
try
|
||||
{
|
||||
KeyGesture keyGesture = new KeyGesture(CharKey, ModifierKeys);
|
||||
}
|
||||
catch (System.Exception e) when (e is NotSupportedException || e is InvalidEnumArgumentException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ModifierKeys == ModifierKeys.None)
|
||||
{
|
||||
return !IsPrintableCharacter(CharKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
private static bool IsPrintableCharacter(Key key)
|
||||
{
|
||||
// https://stackoverflow.com/questions/11881199/identify-if-a-event-key-is-text-not-only-alphanumeric
|
||||
return (key >= Key.A && key <= Key.Z) ||
|
||||
(key >= Key.D0 && key <= Key.D9) ||
|
||||
(key >= Key.NumPad0 && key <= Key.NumPad9) ||
|
||||
key == Key.OemQuestion ||
|
||||
key == Key.OemQuotes ||
|
||||
key == Key.OemPlus ||
|
||||
key == Key.OemOpenBrackets ||
|
||||
key == Key.OemCloseBrackets ||
|
||||
key == Key.OemMinus ||
|
||||
key == Key.DeadCharProcessed ||
|
||||
key == Key.Oem1 ||
|
||||
key == Key.Oem7 ||
|
||||
key == Key.OemPeriod ||
|
||||
key == Key.OemComma ||
|
||||
key == Key.OemMinus ||
|
||||
key == Key.Add ||
|
||||
key == Key.Divide ||
|
||||
key == Key.Multiply ||
|
||||
key == Key.Subtract ||
|
||||
key == Key.Oem102 ||
|
||||
key == Key.Decimal;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is HotkeyModel other)
|
||||
{
|
||||
return ModifierKeys == other.ModifierKeys && CharKey == other.CharKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(ModifierKeys, CharKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
var userName when string.IsNullOrEmpty(userName) =>
|
||||
(new Uri($"http://{Proxy.Server}:{Proxy.Port}"), null),
|
||||
_ => (new Uri($"http://{Proxy.Server}:{Proxy.Port}"),
|
||||
new NetworkCredential(Proxy.UserName, Proxy.Password))
|
||||
new NetworkCredential(Proxy.UserName, Proxy.Password))
|
||||
},
|
||||
_ => (null, null)
|
||||
},
|
||||
|
|
@ -79,7 +79,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
catch(UriFormatException e)
|
||||
catch (UriFormatException e)
|
||||
{
|
||||
API.ShowMsg("Please try again", "Unable to parse Http Proxy");
|
||||
Log.Exception("Flow.Launcher.Infrastructure.Http", "Unable to parse Uri", e);
|
||||
|
|
@ -94,7 +94,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
await using var fileStream = new FileStream(filePath, FileMode.CreateNew);
|
||||
await response.Content.CopyToAsync(fileStream);
|
||||
await response.Content.CopyToAsync(fileStream, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -117,7 +117,7 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
public static Task<string> GetAsync([NotNull] string url, CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
return GetAsync(new Uri(url.Replace("#", "%23")), token);
|
||||
return GetAsync(new Uri(url), token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -130,36 +130,57 @@ namespace Flow.Launcher.Infrastructure.Http
|
|||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
using var response = await client.GetAsync(url, token);
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
if (response.StatusCode == HttpStatusCode.OK)
|
||||
{
|
||||
return content;
|
||||
}
|
||||
else
|
||||
var content = await response.Content.ReadAsStringAsync(token);
|
||||
if (response.StatusCode != HttpStatusCode.OK)
|
||||
{
|
||||
throw new HttpRequestException(
|
||||
$"Error code <{response.StatusCode}> with content <{content}> returned from <{url}>");
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchrously get the result as stream from url.
|
||||
/// Send a GET request to the specified Uri with an HTTP completion option and a cancellation token as an asynchronous operation.
|
||||
/// </summary>
|
||||
/// <param name="url">The Uri the request is sent to.</param>
|
||||
/// <param name="completionOption">An HTTP completion option value that indicates when the operation should be considered completed.</param>
|
||||
/// <param name="token">A cancellation token that can be used by other objects or threads to receive notice of cancellation</param>
|
||||
/// <returns></returns>
|
||||
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>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
public static async Task<Stream> GetStreamAsync([NotNull] string url, CancellationToken token = default)
|
||||
public static async Task<Stream> GetStreamAsync([NotNull] Uri url,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
return await response.Content.ReadAsStreamAsync();
|
||||
return await client.GetStreamAsync(url, token);
|
||||
}
|
||||
|
||||
public static async Task<HttpResponseMessage> GetResponseAsync(string url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||||
CancellationToken token = default)
|
||||
=> await GetResponseAsync(new Uri(url), completionOption, token);
|
||||
|
||||
public static async Task<HttpResponseMessage> GetResponseAsync([NotNull] Uri url, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead,
|
||||
CancellationToken token = default)
|
||||
{
|
||||
Log.Debug($"|Http.Get|Url <{url}>");
|
||||
return await client.GetAsync(url, completionOption, token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asynchrously send an HTTP request.
|
||||
/// </summary>
|
||||
public static async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken token = default)
|
||||
public static async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken token = default)
|
||||
{
|
||||
return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token);
|
||||
return await client.SendAsync(request, completionOption, token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Image
|
||||
|
|
@ -25,11 +24,11 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
public class ImageCache
|
||||
{
|
||||
private const int MaxCached = 50;
|
||||
public ConcurrentDictionary<string, ImageUsage> Data { get; private set; } = new ConcurrentDictionary<string, ImageUsage>();
|
||||
public ConcurrentDictionary<(string, bool), ImageUsage> Data { get; } = new();
|
||||
private const int permissibleFactor = 2;
|
||||
private SemaphoreSlim semaphore = new(1, 1);
|
||||
|
||||
public void Initialization(Dictionary<string, int> usage)
|
||||
public void Initialization(Dictionary<(string, bool), int> usage)
|
||||
{
|
||||
foreach (var key in usage.Keys)
|
||||
{
|
||||
|
|
@ -37,29 +36,29 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
}
|
||||
}
|
||||
|
||||
public ImageSource this[string path]
|
||||
public ImageSource this[string path, bool isFullImage = false]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Data.TryGetValue(path, out var value))
|
||||
if (!Data.TryGetValue((path, isFullImage), out var value))
|
||||
{
|
||||
value.usage++;
|
||||
return value.imageSource;
|
||||
return null;
|
||||
}
|
||||
value.usage++;
|
||||
return value.imageSource;
|
||||
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
Data.AddOrUpdate(
|
||||
path,
|
||||
new ImageUsage(0, value),
|
||||
(k, v) =>
|
||||
{
|
||||
v.imageSource = value;
|
||||
v.usage++;
|
||||
return v;
|
||||
}
|
||||
(path, isFullImage),
|
||||
new ImageUsage(0, value),
|
||||
(k, v) =>
|
||||
{
|
||||
v.imageSource = value;
|
||||
v.usage++;
|
||||
return v;
|
||||
}
|
||||
);
|
||||
|
||||
SliceExtra();
|
||||
|
|
@ -82,9 +81,24 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
}
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key)
|
||||
public bool ContainsKey(string key, bool isFullImage)
|
||||
{
|
||||
return key is not null && Data.ContainsKey(key) && Data[key].imageSource != null;
|
||||
return key is not null && Data.ContainsKey((key, isFullImage)) && Data[(key, isFullImage)].imageSource != null;
|
||||
}
|
||||
|
||||
public bool TryGetValue(string key, bool isFullImage, out ImageSource image)
|
||||
{
|
||||
if (key is not null)
|
||||
{
|
||||
bool hasKey = Data.TryGetValue((key, isFullImage), out var imageUsage);
|
||||
image = hasKey ? imageUsage.imageSource : null;
|
||||
return hasKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
image = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int CacheSize()
|
||||
|
|
@ -100,4 +114,4 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return Data.Values.Select(x => x.imageSource).Distinct().Count();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Flow.Launcher.Infrastructure.Logger;
|
||||
using Flow.Launcher.Infrastructure.Storage;
|
||||
using static Flow.Launcher.Infrastructure.Http.Http;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.Image
|
||||
{
|
||||
public static class ImageLoader
|
||||
{
|
||||
private static readonly ImageCache ImageCache = new();
|
||||
private static BinaryStorage<Dictionary<string, int>> _storage;
|
||||
private static BinaryStorage<Dictionary<(string, bool), int>> _storage;
|
||||
private static readonly ConcurrentDictionary<string, string> GuidToKey = new();
|
||||
private static IImageHashGenerator _hashGenerator;
|
||||
private static readonly bool EnableImageHash = true;
|
||||
public static ImageSource DefaultImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
public static ImageSource MissingImage { get; } = new BitmapImage(new Uri(Constant.MissingImgIcon));
|
||||
public static ImageSource LoadingImage { get; } = new BitmapImage(new Uri(Constant.LoadingImgIcon));
|
||||
public const int SmallIconSize = 64;
|
||||
public const int FullIconSize = 256;
|
||||
|
||||
|
||||
private static readonly string[] ImageExtensions =
|
||||
{
|
||||
".png",
|
||||
".jpg",
|
||||
".jpeg",
|
||||
".gif",
|
||||
".bmp",
|
||||
".tiff",
|
||||
".ico"
|
||||
".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico"
|
||||
};
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
_storage = new BinaryStorage<Dictionary<string, int>>("Image");
|
||||
_storage = new BinaryStorage<Dictionary<(string, bool), int>>("Image");
|
||||
_hashGenerator = new ImageHashGenerator();
|
||||
|
||||
var usage = LoadStorageToConcurrentDictionary();
|
||||
|
||||
foreach (var icon in new[] { Constant.DefaultIcon, Constant.MissingImgIcon })
|
||||
foreach (var icon in new[]
|
||||
{
|
||||
Constant.DefaultIcon, Constant.MissingImgIcon
|
||||
})
|
||||
{
|
||||
ImageSource img = new BitmapImage(new Uri(icon));
|
||||
img.Freeze();
|
||||
ImageCache[icon] = img;
|
||||
ImageCache[icon, false] = img;
|
||||
}
|
||||
|
||||
_ = Task.Run(() =>
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
Stopwatch.Normal("|ImageLoader.Initialize|Preload images cost", () =>
|
||||
await Stopwatch.NormalAsync("|ImageLoader.Initialize|Preload images cost", async () =>
|
||||
{
|
||||
ImageCache.Data.AsParallel().ForAll(x =>
|
||||
foreach (var ((path, isFullImage), _) in ImageCache.Data)
|
||||
{
|
||||
Load(x.Key);
|
||||
});
|
||||
await LoadAsync(path, isFullImage);
|
||||
}
|
||||
});
|
||||
Log.Info($"|ImageLoader.Initialize|Number of preload images is <{ImageCache.CacheSize()}>, Images Number: {ImageCache.CacheSize()}, Unique Items {ImageCache.UniqueImagesInCache()}");
|
||||
});
|
||||
|
|
@ -63,17 +67,20 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
lock (_storage)
|
||||
{
|
||||
_storage.Save(ImageCache.Data.Select(x => (x.Key, x.Value.usage)).ToDictionary(x => x.Key, x => x.usage));
|
||||
_storage.Save(ImageCache.Data
|
||||
.ToDictionary(
|
||||
x => x.Key,
|
||||
x => x.Value.usage));
|
||||
}
|
||||
}
|
||||
|
||||
private static ConcurrentDictionary<string, int> LoadStorageToConcurrentDictionary()
|
||||
private static ConcurrentDictionary<(string, bool), int> LoadStorageToConcurrentDictionary()
|
||||
{
|
||||
lock (_storage)
|
||||
{
|
||||
var loaded = _storage.TryLoad(new Dictionary<string, int>());
|
||||
var loaded = _storage.TryLoad(new Dictionary<(string, bool), int>());
|
||||
|
||||
return new ConcurrentDictionary<string, int>(loaded);
|
||||
return new ConcurrentDictionary<(string, bool), int>(loaded);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,11 +102,12 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
Folder,
|
||||
Data,
|
||||
ImageFile,
|
||||
FullImageFile,
|
||||
Error,
|
||||
Cache
|
||||
}
|
||||
|
||||
private static ImageResult LoadInternal(string path, bool loadFullImage = false)
|
||||
private static async ValueTask<ImageResult> LoadInternalAsync(string path, bool loadFullImage = false)
|
||||
{
|
||||
ImageResult imageResult;
|
||||
|
||||
|
|
@ -107,13 +115,21 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
return new ImageResult(ImageCache[Constant.MissingImgIcon], ImageType.Error);
|
||||
}
|
||||
if (ImageCache.ContainsKey(path))
|
||||
{
|
||||
return new ImageResult(ImageCache[path], ImageType.Cache);
|
||||
return new ImageResult(MissingImage, ImageType.Error);
|
||||
}
|
||||
|
||||
if (ImageCache.ContainsKey(path, loadFullImage))
|
||||
{
|
||||
return new ImageResult(ImageCache[path, loadFullImage], ImageType.Cache);
|
||||
}
|
||||
|
||||
if (Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out var uriResult)
|
||||
&& (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps))
|
||||
{
|
||||
var image = await LoadRemoteImageAsync(loadFullImage, uriResult);
|
||||
ImageCache[path, loadFullImage] = image;
|
||||
return new ImageResult(image, ImageType.ImageFile);
|
||||
}
|
||||
if (path.StartsWith("data:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var imageSource = new BitmapImage(new Uri(path));
|
||||
|
|
@ -121,12 +137,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return new ImageResult(imageSource, ImageType.Data);
|
||||
}
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
{
|
||||
path = Path.Combine(Constant.ProgramDirectory, "Images", Path.GetFileName(path));
|
||||
}
|
||||
|
||||
imageResult = GetThumbnailResult(ref path, loadFullImage);
|
||||
imageResult = await Task.Run(() => GetThumbnailResult(ref path, loadFullImage));
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
|
|
@ -140,14 +151,35 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
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);
|
||||
|
||||
ImageSource image = ImageCache[Constant.MissingImgIcon];
|
||||
ImageCache[path] = image;
|
||||
ImageSource image = ImageCache[Constant.MissingImgIcon, false];
|
||||
ImageCache[path, false] = image;
|
||||
imageResult = new ImageResult(image, ImageType.Error);
|
||||
}
|
||||
}
|
||||
|
||||
return imageResult;
|
||||
}
|
||||
private static async Task<BitmapImage> LoadRemoteImageAsync(bool loadFullImage, Uri uriResult)
|
||||
{
|
||||
// Download image from url
|
||||
await using var resp = await GetStreamAsync(uriResult);
|
||||
await using var buffer = new MemoryStream();
|
||||
await resp.CopyToAsync(buffer);
|
||||
buffer.Seek(0, SeekOrigin.Begin);
|
||||
var image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
if (!loadFullImage)
|
||||
{
|
||||
image.DecodePixelHeight = SmallIconSize;
|
||||
image.DecodePixelWidth = SmallIconSize;
|
||||
}
|
||||
image.StreamSource = buffer;
|
||||
image.EndInit();
|
||||
image.StreamSource = null;
|
||||
image.Freeze();
|
||||
return image;
|
||||
}
|
||||
|
||||
private static ImageResult GetThumbnailResult(ref string path, bool loadFullImage = false)
|
||||
{
|
||||
|
|
@ -173,6 +205,7 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
if (loadFullImage)
|
||||
{
|
||||
image = LoadFullImage(path);
|
||||
type = ImageType.FullImageFile;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -187,12 +220,12 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
else
|
||||
{
|
||||
type = ImageType.File;
|
||||
image = GetThumbnail(path, ThumbnailOptions.None);
|
||||
image = GetThumbnail(path, ThumbnailOptions.None, loadFullImage ? FullIconSize : SmallIconSize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
image = ImageCache[Constant.MissingImgIcon];
|
||||
image = ImageCache[Constant.MissingImgIcon, false];
|
||||
path = Constant.MissingImgIcon;
|
||||
}
|
||||
|
||||
|
|
@ -204,23 +237,28 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
return new ImageResult(image, type);
|
||||
}
|
||||
|
||||
private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = ThumbnailOptions.ThumbnailOnly)
|
||||
private static BitmapSource GetThumbnail(string path, ThumbnailOptions option = ThumbnailOptions.ThumbnailOnly, int size = SmallIconSize)
|
||||
{
|
||||
return WindowsThumbnailProvider.GetThumbnail(
|
||||
path,
|
||||
Constant.ThumbnailSize,
|
||||
Constant.ThumbnailSize,
|
||||
size,
|
||||
size,
|
||||
option);
|
||||
}
|
||||
|
||||
public static bool CacheContainImage(string path)
|
||||
public static bool CacheContainImage(string path, bool loadFullImage = false)
|
||||
{
|
||||
return ImageCache.ContainsKey(path) && ImageCache[path] != null;
|
||||
return ImageCache.ContainsKey(path, loadFullImage);
|
||||
}
|
||||
|
||||
public static ImageSource Load(string path, bool loadFullImage = false)
|
||||
public static bool TryGetValue(string path, bool loadFullImage, out ImageSource image)
|
||||
{
|
||||
var imageResult = LoadInternal(path, loadFullImage);
|
||||
return ImageCache.TryGetValue(path, loadFullImage, out image);
|
||||
}
|
||||
|
||||
public static async ValueTask<ImageSource> LoadAsync(string path, bool loadFullImage = false)
|
||||
{
|
||||
var imageResult = await LoadInternalAsync(path, loadFullImage);
|
||||
|
||||
var img = imageResult.ImageSource;
|
||||
if (imageResult.ImageType != ImageType.Error && imageResult.ImageType != ImageType.Cache)
|
||||
|
|
@ -231,16 +269,17 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
|
||||
if (GuidToKey.TryGetValue(hash, out string key))
|
||||
{ // image already exists
|
||||
img = ImageCache[key] ?? img;
|
||||
img = ImageCache[key, false] ?? img;
|
||||
}
|
||||
else
|
||||
{ // new guid
|
||||
|
||||
GuidToKey[hash] = path;
|
||||
}
|
||||
}
|
||||
|
||||
// update cache
|
||||
ImageCache[path] = img;
|
||||
ImageCache[path, loadFullImage] = img;
|
||||
}
|
||||
|
||||
return img;
|
||||
|
|
@ -251,8 +290,33 @@ namespace Flow.Launcher.Infrastructure.Image
|
|||
BitmapImage image = new BitmapImage();
|
||||
image.BeginInit();
|
||||
image.CacheOption = BitmapCacheOption.OnLoad;
|
||||
image.UriSource = new Uri(path);
|
||||
image.UriSource = new Uri(path);
|
||||
image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
|
||||
image.EndInit();
|
||||
|
||||
if (image.PixelWidth > 320)
|
||||
{
|
||||
BitmapImage resizedWidth = new BitmapImage();
|
||||
resizedWidth.BeginInit();
|
||||
resizedWidth.CacheOption = BitmapCacheOption.OnLoad;
|
||||
resizedWidth.UriSource = new Uri(path);
|
||||
resizedWidth.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
|
||||
resizedWidth.DecodePixelWidth = 320;
|
||||
resizedWidth.EndInit();
|
||||
|
||||
if (resizedWidth.PixelHeight > 320)
|
||||
{
|
||||
BitmapImage resizedHeight = new BitmapImage();
|
||||
resizedHeight.BeginInit();
|
||||
resizedHeight.CacheOption = BitmapCacheOption.OnLoad;
|
||||
resizedHeight.UriSource = new Uri(path);
|
||||
resizedHeight.CreateOptions = BitmapCreateOptions.IgnoreColorProfile;
|
||||
resizedHeight.DecodePixelHeight = 320;
|
||||
resizedHeight.EndInit();
|
||||
return resizedHeight;
|
||||
}
|
||||
return resizedWidth;
|
||||
}
|
||||
return image;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using NLog;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
|
||||
private List<int> originalIndexs = new List<int>();
|
||||
private List<int> translatedIndexs = new List<int>();
|
||||
private int translaedLength = 0;
|
||||
private int translatedLength = 0;
|
||||
|
||||
public string key { get; private set; }
|
||||
|
||||
|
|
@ -32,13 +32,13 @@ namespace Flow.Launcher.Infrastructure
|
|||
originalIndexs.Add(originalIndex);
|
||||
translatedIndexs.Add(translatedIndex);
|
||||
translatedIndexs.Add(translatedIndex + length);
|
||||
translaedLength += length - 1;
|
||||
translatedLength += length - 1;
|
||||
}
|
||||
|
||||
public int MapToOriginalIndex(int translatedIndex)
|
||||
{
|
||||
if (translatedIndex > translatedIndexs.Last())
|
||||
return translatedIndex - translaedLength - 1;
|
||||
return translatedIndex - translatedLength - 1;
|
||||
|
||||
int lowerBound = 0;
|
||||
int upperBound = originalIndexs.Count - 1;
|
||||
|
|
@ -83,7 +83,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
translatedIndex < translatedIndexs[upperBound * 2])
|
||||
{
|
||||
int indexDef = 0;
|
||||
|
||||
|
||||
for (int j = 0; j < upperBound; j++)
|
||||
{
|
||||
indexDef += translatedIndexs[j * 2 + 1] - translatedIndexs[j * 2];
|
||||
|
|
@ -102,9 +102,24 @@ namespace Flow.Launcher.Infrastructure
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translate a language to English letters using a given rule.
|
||||
/// </summary>
|
||||
public interface IAlphabet
|
||||
{
|
||||
/// <summary>
|
||||
/// Translate a string to English letters, using a given rule.
|
||||
/// </summary>
|
||||
/// <param name="stringToTranslate">String to translate.</param>
|
||||
/// <returns></returns>
|
||||
public (string translation, TranslationMapping map) Translate(string stringToTranslate);
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a string can be translated to English letter with this Alphabet.
|
||||
/// </summary>
|
||||
/// <param name="stringToTranslate">String to translate.</param>
|
||||
/// <returns></returns>
|
||||
public bool CanBeTranslated(string stringToTranslate);
|
||||
}
|
||||
|
||||
public class PinyinAlphabet : IAlphabet
|
||||
|
|
@ -119,63 +134,70 @@ namespace Flow.Launcher.Infrastructure
|
|||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
}
|
||||
|
||||
public bool CanBeTranslated(string stringToTranslate)
|
||||
{
|
||||
return WordsHelper.HasChinese(stringToTranslate);
|
||||
}
|
||||
|
||||
public (string translation, TranslationMapping map) Translate(string content)
|
||||
{
|
||||
if (_settings.ShouldUsePinyin)
|
||||
{
|
||||
if (!_pinyinCache.ContainsKey(content))
|
||||
{
|
||||
if (WordsHelper.HasChinese(content))
|
||||
{
|
||||
var resultList = WordsHelper.GetPinyinList(content);
|
||||
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
TranslationMapping map = new TranslationMapping();
|
||||
|
||||
bool pre = false;
|
||||
|
||||
for (int i = 0; i < resultList.Length; i++)
|
||||
{
|
||||
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
|
||||
{
|
||||
map.AddNewIndex(i, resultBuilder.Length, resultList[i].Length + 1);
|
||||
resultBuilder.Append(' ');
|
||||
resultBuilder.Append(resultList[i]);
|
||||
pre = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pre)
|
||||
{
|
||||
pre = false;
|
||||
resultBuilder.Append(' ');
|
||||
}
|
||||
|
||||
resultBuilder.Append(resultList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
map.endConstruct();
|
||||
|
||||
var key = resultBuilder.ToString();
|
||||
map.setKey(key);
|
||||
|
||||
return _pinyinCache[content] = (key, map);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (content, null);
|
||||
}
|
||||
return BuildCacheFromContent(content);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _pinyinCache[content];
|
||||
}
|
||||
}
|
||||
return (content, null);
|
||||
}
|
||||
|
||||
private (string translation, TranslationMapping map) BuildCacheFromContent(string content)
|
||||
{
|
||||
if (WordsHelper.HasChinese(content))
|
||||
{
|
||||
var resultList = WordsHelper.GetPinyinList(content);
|
||||
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
TranslationMapping map = new TranslationMapping();
|
||||
|
||||
bool pre = false;
|
||||
|
||||
for (int i = 0; i < resultList.Length; i++)
|
||||
{
|
||||
if (content[i] >= 0x3400 && content[i] <= 0x9FD5)
|
||||
{
|
||||
map.AddNewIndex(i, resultBuilder.Length, resultList[i].Length + 1);
|
||||
resultBuilder.Append(' ');
|
||||
resultBuilder.Append(resultList[i]);
|
||||
pre = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pre)
|
||||
{
|
||||
pre = false;
|
||||
resultBuilder.Append(' ');
|
||||
}
|
||||
|
||||
resultBuilder.Append(resultList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
map.endConstruct();
|
||||
|
||||
var key = resultBuilder.ToString();
|
||||
map.setKey(key);
|
||||
|
||||
return _pinyinCache[content] = (key, map);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (content, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
|
|
@ -11,62 +12,86 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
/// </summary>
|
||||
public class JsonStorage<T> where T : new()
|
||||
{
|
||||
protected T _data;
|
||||
protected T? Data;
|
||||
|
||||
// need a new directory name
|
||||
public const string DirectoryName = "Settings";
|
||||
public const string FileSuffix = ".json";
|
||||
public string FilePath { get; set; }
|
||||
public string DirectoryPath { get; set; }
|
||||
|
||||
protected string FilePath { get; init; } = null!;
|
||||
|
||||
private string TempFilePath => $"{FilePath}.tmp";
|
||||
|
||||
private string BackupFilePath => $"{FilePath}.bak";
|
||||
|
||||
protected string DirectoryPath { get; init; } = null!;
|
||||
|
||||
|
||||
public T Load()
|
||||
{
|
||||
string? serialized = null;
|
||||
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
var serialized = File.ReadAllText(FilePath);
|
||||
if (!string.IsNullOrWhiteSpace(serialized))
|
||||
serialized = File.ReadAllText(FilePath);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(serialized))
|
||||
{
|
||||
try
|
||||
{
|
||||
Deserialize(serialized);
|
||||
Data = JsonSerializer.Deserialize<T>(serialized) ?? TryLoadBackup() ?? LoadDefault();
|
||||
}
|
||||
else
|
||||
catch (JsonException)
|
||||
{
|
||||
LoadDefault();
|
||||
Data = TryLoadBackup() ?? LoadDefault();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LoadDefault();
|
||||
Data = TryLoadBackup() ?? LoadDefault();
|
||||
}
|
||||
return _data.NonNull();
|
||||
|
||||
return Data.NonNull();
|
||||
}
|
||||
|
||||
private void Deserialize(string serialized)
|
||||
{
|
||||
try
|
||||
{
|
||||
_data = JsonSerializer.Deserialize<T>(serialized);
|
||||
}
|
||||
catch (JsonException e)
|
||||
{
|
||||
LoadDefault();
|
||||
Log.Exception($"|JsonStorage.Deserialize|Deserialize error for json <{FilePath}>", e);
|
||||
}
|
||||
|
||||
if (_data == null)
|
||||
{
|
||||
LoadDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadDefault()
|
||||
private T LoadDefault()
|
||||
{
|
||||
if (File.Exists(FilePath))
|
||||
{
|
||||
BackupOriginFile();
|
||||
}
|
||||
|
||||
_data = new T();
|
||||
Save();
|
||||
return new T();
|
||||
}
|
||||
|
||||
private T? TryLoadBackup()
|
||||
{
|
||||
if (!File.Exists(BackupFilePath))
|
||||
return default;
|
||||
|
||||
try
|
||||
{
|
||||
var data = JsonSerializer.Deserialize<T>(File.ReadAllText(BackupFilePath));
|
||||
|
||||
if (data != null)
|
||||
{
|
||||
Log.Info($"|JsonStorage.Load|Failed to load settings.json, {BackupFilePath} restored successfully");
|
||||
|
||||
if(File.Exists(FilePath))
|
||||
File.Replace(BackupFilePath, FilePath, null);
|
||||
else
|
||||
File.Move(BackupFilePath, FilePath);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private void BackupOriginFile()
|
||||
|
|
@ -82,13 +107,22 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
public void Save()
|
||||
{
|
||||
string serialized = JsonSerializer.Serialize(_data, new JsonSerializerOptions() { WriteIndented = true });
|
||||
string serialized = JsonSerializer.Serialize(Data,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
WriteIndented = true
|
||||
});
|
||||
|
||||
File.WriteAllText(FilePath, serialized);
|
||||
File.WriteAllText(TempFilePath, serialized);
|
||||
|
||||
if (!File.Exists(FilePath))
|
||||
{
|
||||
File.Move(TempFilePath, FilePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Replace(TempFilePath, FilePath, BackupFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Deprecated as of Flow Launcher v1.8.0, on 2021.06.21. " +
|
||||
"This is used only for Everything plugin v1.4.9 or below backwards compatibility")]
|
||||
public class JsonStrorage<T> : JsonStorage<T> where T : new() { }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Flow.Launcher.Infrastructure.Storage
|
|||
|
||||
public PluginJsonStorage(T data) : this()
|
||||
{
|
||||
_data = data;
|
||||
Data = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
|
@ -60,8 +60,13 @@ namespace Flow.Launcher.Infrastructure
|
|||
return new MatchResult(false, UserSettingSearchPrecision);
|
||||
|
||||
query = query.Trim();
|
||||
TranslationMapping translationMapping;
|
||||
(stringToCompare, translationMapping) = _alphabet?.Translate(stringToCompare) ?? (stringToCompare, null);
|
||||
TranslationMapping translationMapping = null;
|
||||
if (_alphabet is not null && !_alphabet.CanBeTranslated(query))
|
||||
{
|
||||
// We assume that if a query can be translated (containing characters of a language, like Chinese)
|
||||
// it actually means user doesn't want it to be translated to English letters.
|
||||
(stringToCompare, translationMapping) = _alphabet.Translate(stringToCompare);
|
||||
}
|
||||
|
||||
var currentAcronymQueryIndex = 0;
|
||||
var acronymMatchData = new List<int>();
|
||||
|
|
@ -202,7 +207,11 @@ namespace Flow.Launcher.Infrastructure
|
|||
if (allQuerySubstringsMatched)
|
||||
{
|
||||
var nearestSpaceIndex = CalculateClosestSpaceIndex(spaceIndices, firstMatchIndex);
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1,
|
||||
|
||||
// firstMatchIndex - nearestSpaceIndex - 1 is to set the firstIndex as the index of the first matched char
|
||||
// preceded by a space e.g. 'world' matching 'hello world' firstIndex would be 0 not 6
|
||||
// giving more weight than 'we or donald' by allowing the distance calculation to treat the starting position at after the space.
|
||||
var score = CalculateSearchScore(query, stringToCompare, firstMatchIndex - nearestSpaceIndex - 1, spaceIndices,
|
||||
lastMatchIndex - firstMatchIndex, allSubstringsContainedInCompareString);
|
||||
|
||||
var resultList = indexList.Select(x => translationMapping?.MapToOriginalIndex(x) ?? x).Distinct().ToList();
|
||||
|
|
@ -296,7 +305,7 @@ namespace Flow.Launcher.Infrastructure
|
|||
return currentQuerySubstringIndex >= querySubstringsLength;
|
||||
}
|
||||
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, int matchLen,
|
||||
private static int CalculateSearchScore(string query, string stringToCompare, int firstIndex, List<int> spaceIndices, int matchLen,
|
||||
bool allSubstringsContainedInCompareString)
|
||||
{
|
||||
// A match found near the beginning of a string is scored more than a match found near the end
|
||||
|
|
@ -304,6 +313,14 @@ namespace Flow.Launcher.Infrastructure
|
|||
// while the score is lower if they are more spread out
|
||||
var score = 100 * (query.Length + 1) / ((1 + firstIndex) + (matchLen + 1));
|
||||
|
||||
// Give more weight to a match that is closer to the start of the string.
|
||||
// if the first matched char is immediately before space and all strings are contained in the compare string e.g. 'world' matching 'hello world'
|
||||
// and 'world hello', because both have 'world' immediately preceded by space, their firstIndex will be 0 when distance is calculated,
|
||||
// to prevent them scoring the same, we adjust the score by deducting the number of spaces it has from the start of the string, so 'world hello'
|
||||
// will score slightly higher than 'hello world' because 'hello world' has one additional space.
|
||||
if (firstIndex == 0 && allSubstringsContainedInCompareString)
|
||||
score -= spaceIndices.Count;
|
||||
|
||||
// A match with less characters assigning more weights
|
||||
if (stringToCompare.Length - query.Length < 5)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
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 &&
|
||||
Key == other.Key;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Key.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomShortcutModel : ShortcutBaseModel
|
||||
{
|
||||
public string Value { get; set; }
|
||||
|
||||
[JsonConstructorAttribute]
|
||||
public CustomShortcutModel(string key, string value)
|
||||
{
|
||||
Key = key;
|
||||
Value = value;
|
||||
Expand = () => { return Value; };
|
||||
}
|
||||
|
||||
public void Deconstruct(out string key, out string value)
|
||||
{
|
||||
key = Key;
|
||||
value = Value;
|
||||
}
|
||||
|
||||
public static implicit operator (string Key, string Value)(CustomShortcutModel shortcut)
|
||||
{
|
||||
return (shortcut.Key, shortcut.Value);
|
||||
}
|
||||
|
||||
public static implicit operator CustomShortcutModel((string Key, string Value) shortcut)
|
||||
{
|
||||
return new CustomShortcutModel(shortcut.Key, shortcut.Value);
|
||||
}
|
||||
}
|
||||
|
||||
public class BuiltinShortcutModel : ShortcutBaseModel
|
||||
{
|
||||
public string Description { get; set; }
|
||||
|
||||
public BuiltinShortcutModel(string key, string description, Func<string> expand)
|
||||
{
|
||||
Key = key;
|
||||
Description = description;
|
||||
Expand = expand ?? (() => { return ""; });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,5 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
|
|
@ -31,5 +27,10 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public static readonly string PluginsDirectory = Path.Combine(DataDirectory(), Constant.Plugins);
|
||||
public static readonly string PluginSettingsDirectory = Path.Combine(DataDirectory(), "Settings", Constant.Plugins);
|
||||
|
||||
public const string PythonEnvironmentName = "Python";
|
||||
public const string NodeEnvironmentName = "Node.js";
|
||||
public const string PluginEnvironments = "Environments";
|
||||
public static readonly string PluginEnvironmentsPath = Path.Combine(DataDirectory(), PluginEnvironments);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,34 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
{
|
||||
public class PluginsSettings : BaseModel
|
||||
{
|
||||
private string pythonExecutablePath = string.Empty;
|
||||
public string PythonExecutablePath {
|
||||
get { return pythonExecutablePath; }
|
||||
set
|
||||
{
|
||||
pythonExecutablePath = value;
|
||||
Constant.PythonPath = value;
|
||||
}
|
||||
}
|
||||
|
||||
private string nodeExecutablePath = string.Empty;
|
||||
public string NodeExecutablePath
|
||||
{
|
||||
get { return nodeExecutablePath; }
|
||||
set
|
||||
{
|
||||
nodeExecutablePath = value;
|
||||
Constant.NodePath = value;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove. This is backwards compatibility for 1.10.0 release.
|
||||
public string PythonDirectory { get; set; }
|
||||
|
||||
public Dictionary<string, Plugin> Plugins { get; set; } = new Dictionary<string, Plugin>();
|
||||
|
||||
public void UpdatePluginSettings(List<PluginMetadata> metadatas)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Plugin;
|
||||
using Flow.Launcher.Plugin.SharedModels;
|
||||
using Flow.Launcher;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
namespace Flow.Launcher.Infrastructure.UserSettings
|
||||
|
|
@ -13,11 +13,13 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public class Settings : BaseModel
|
||||
{
|
||||
private string language = "en";
|
||||
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";
|
||||
public bool ShowOpenResultHotkey { get; set; } = true;
|
||||
public double WindowSize { get; set; } = 580;
|
||||
public string PreviewHotkey { get; set; } = $"F1";
|
||||
|
||||
public string Language
|
||||
{
|
||||
|
|
@ -28,7 +30,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
public string Theme { get; set; } = Constant.DefaultTheme;
|
||||
public string Theme
|
||||
{
|
||||
get => _theme;
|
||||
set
|
||||
{
|
||||
if (value == _theme)
|
||||
return;
|
||||
_theme = value;
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(MaxResultsToShow));
|
||||
}
|
||||
}
|
||||
public bool UseDropShadowEffect { get; set; } = false;
|
||||
public string QueryBoxFont { get; set; } = FontFamily.GenericSansSerif.Name;
|
||||
public string QueryBoxFontStyle { get; set; }
|
||||
|
|
@ -41,8 +54,18 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
public bool UseGlyphIcons { get; set; } = true;
|
||||
public bool UseAnimation { get; set; } = true;
|
||||
public bool UseSound { get; set; } = true;
|
||||
public bool UseClock { get; set; } = true;
|
||||
public bool UseDate { get; set; } = false;
|
||||
public string TimeFormat { get; set; } = "hh:mm tt";
|
||||
public string DateFormat { get; set; } = "MM'/'dd ddd";
|
||||
public bool FirstLaunch { get; set; } = true;
|
||||
|
||||
public double SettingWindowWidth { get; set; } = 1000;
|
||||
public double SettingWindowHeight { get; set; } = 700;
|
||||
public double SettingWindowTop { get; set; }
|
||||
public double SettingWindowLeft { get; set; }
|
||||
public System.Windows.WindowState SettingWindowState { get; set; } = WindowState.Normal;
|
||||
|
||||
public int CustomExplorerIndex { get; set; } = 0;
|
||||
|
||||
[JsonIgnore]
|
||||
|
|
@ -120,8 +143,7 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
PrivateArg = "-private",
|
||||
EnablePrivate = false,
|
||||
Editable = false
|
||||
}
|
||||
,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "MS Edge",
|
||||
|
|
@ -137,6 +159,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
/// when false Alphabet static service will always return empty results
|
||||
/// </summary>
|
||||
public bool ShouldUsePinyin { get; set; } = false;
|
||||
public bool AlwaysPreview { get; set; } = false;
|
||||
public bool AlwaysStartEn { get; set; } = false;
|
||||
|
||||
[JsonInclude, JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchPrecisionScore QuerySearchPrecision { get; private set; } = SearchPrecisionScore.Regular;
|
||||
|
|
@ -171,12 +195,32 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
|
||||
public double WindowLeft { get; set; }
|
||||
public double WindowTop { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom left position on selected monitor
|
||||
/// </summary>
|
||||
public double CustomWindowLeft { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Custom top position on selected monitor
|
||||
/// </summary>
|
||||
public double CustomWindowTop { get; set; } = 0;
|
||||
|
||||
public int MaxResultsToShow { get; set; } = 5;
|
||||
public int ActivateTimes { get; set; }
|
||||
|
||||
|
||||
public ObservableCollection<CustomPluginHotkey> CustomPluginHotkeys { get; set; } = new ObservableCollection<CustomPluginHotkey>();
|
||||
|
||||
public ObservableCollection<CustomShortcutModel> CustomShortcuts { get; set; } = new ObservableCollection<CustomShortcutModel>();
|
||||
|
||||
[JsonIgnore]
|
||||
public ObservableCollection<BuiltinShortcutModel> BuiltinShortcuts { get; set; } = new()
|
||||
{
|
||||
new BuiltinShortcutModel("{clipboard}", "shortcut_clipboard_description", Clipboard.GetText),
|
||||
new BuiltinShortcutModel("{active_explorer_path}", "shortcut_active_explorer_path", FileExplorerHelper.GetActiveExplorerPath)
|
||||
};
|
||||
|
||||
public bool DontPromptUpdateMsg { get; set; }
|
||||
public bool EnableUpdateLog { get; set; }
|
||||
|
||||
|
|
@ -193,8 +237,16 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
}
|
||||
}
|
||||
public bool LeaveCmdOpen { get; set; }
|
||||
public bool HideWhenDeactive { get; set; } = true;
|
||||
public bool RememberLastLaunchLocation { get; set; }
|
||||
public bool HideWhenDeactivated { get; set; } = true;
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.RememberLastLaunchLocation;
|
||||
|
||||
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||
public SearchWindowAligns SearchWindowAlign { get; set; } = SearchWindowAligns.Center;
|
||||
|
||||
public int CustomScreenNumber { get; set; } = 1;
|
||||
|
||||
public bool IgnoreHotkeysOnFullscreen { get; set; }
|
||||
|
||||
public HttpProxy Proxy { get; set; } = new HttpProxy();
|
||||
|
|
@ -220,4 +272,22 @@ namespace Flow.Launcher.Infrastructure.UserSettings
|
|||
Light,
|
||||
Dark
|
||||
}
|
||||
}
|
||||
|
||||
public enum SearchWindowScreens
|
||||
{
|
||||
RememberLastLaunchLocation,
|
||||
Cursor,
|
||||
Focus,
|
||||
Primary,
|
||||
Custom
|
||||
}
|
||||
|
||||
public enum SearchWindowAligns
|
||||
{
|
||||
Center,
|
||||
CenterTop,
|
||||
LeftTop,
|
||||
RightTop,
|
||||
Custom
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,27 +10,37 @@ namespace Flow.Launcher.Plugin
|
|||
/// <summary>
|
||||
/// Python
|
||||
/// </summary>
|
||||
public const string Python = "PYTHON";
|
||||
|
||||
public const string Python = "Python";
|
||||
|
||||
/// <summary>
|
||||
/// Python V2
|
||||
/// </summary>
|
||||
public const string PythonV2 = "PYTHON_V2";
|
||||
public const string PythonV2 = "Python_v2";
|
||||
|
||||
/// <summary>
|
||||
/// C#
|
||||
/// </summary>
|
||||
public const string CSharp = "CSHARP";
|
||||
public const string CSharp = "CSharp";
|
||||
|
||||
/// <summary>
|
||||
/// F#
|
||||
/// </summary>
|
||||
public const string FSharp = "FSHARP";
|
||||
public const string FSharp = "FSharp";
|
||||
|
||||
/// <summary>
|
||||
/// Standard .exe
|
||||
/// </summary>
|
||||
public const string Executable = "EXECUTABLE";
|
||||
public const string Executable = "Executable";
|
||||
|
||||
/// <summary>
|
||||
/// TypeScript
|
||||
/// </summary>
|
||||
public const string TypeScript = "TypeScript";
|
||||
|
||||
/// <summary>
|
||||
/// JavaScript
|
||||
/// </summary>
|
||||
public const string JavaScript = "JavaScript";
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this language is a .NET language
|
||||
|
|
@ -39,8 +49,8 @@ namespace Flow.Launcher.Plugin
|
|||
/// <returns></returns>
|
||||
public static bool IsDotNet(string language)
|
||||
{
|
||||
return language.ToUpper() == CSharp
|
||||
|| language.ToUpper() == FSharp;
|
||||
return language.Equals(CSharp, StringComparison.OrdinalIgnoreCase)
|
||||
|| language.Equals(FSharp, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -51,9 +61,11 @@ namespace Flow.Launcher.Plugin
|
|||
public static bool IsAllowed(string language)
|
||||
{
|
||||
return IsDotNet(language)
|
||||
|| String.Equals(language, Python, StringComparison.CurrentCultureIgnoreCase)
|
||||
|| String.Equals(language, PythonV2, StringComparison.CurrentCultureIgnoreCase)
|
||||
|| String.Equals(language, Executable, StringComparison.CurrentCultureIgnoreCase);
|
||||
|| 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows</TargetFramework>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<ProjectGuid>{8451ECDD-2EA4-4966-BB0A-7BBC40138E80}</ProjectGuid>
|
||||
<UseWPF>true</UseWPF>
|
||||
<OutputType>Library</OutputType>
|
||||
|
|
@ -14,10 +14,10 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>3.0.0</Version>
|
||||
<PackageVersion>3.0.0</PackageVersion>
|
||||
<AssemblyVersion>3.0.0</AssemblyVersion>
|
||||
<FileVersion>3.0.0</FileVersion>
|
||||
<Version>4.0.0</Version>
|
||||
<PackageVersion>4.0.0</PackageVersion>
|
||||
<AssemblyVersion>4.0.0</AssemblyVersion>
|
||||
<FileVersion>4.0.0</FileVersion>
|
||||
<PackageId>Flow.Launcher.Plugin</PackageId>
|
||||
<Authors>Flow-Launcher</Authors>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
|
|
@ -65,8 +65,8 @@
|
|||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" />
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -183,6 +183,13 @@ namespace Flow.Launcher.Plugin
|
|||
/// <param name="oldActionKeyword">The actionkeyword that is supposed to be removed</param>
|
||||
void RemoveActionKeyword(string pluginId, string oldActionKeyword);
|
||||
|
||||
/// <summary>
|
||||
/// Check whether specific ActionKeyword is assigned to any of the plugin
|
||||
/// </summary>
|
||||
/// <param name="actionKeyword">The actionkeyword for checking</param>
|
||||
/// <returns>True if the actionkeyword is already assigned, False otherwise</returns>
|
||||
bool ActionKeywordAssigned(string actionKeyword);
|
||||
|
||||
/// <summary>
|
||||
/// Log debug message
|
||||
/// Message will only be logged in Debug mode
|
||||
|
|
@ -226,8 +233,8 @@ namespace Flow.Launcher.Plugin
|
|||
/// Open directory in an explorer configured by user via Flow's Settings. The default is Windows Explorer
|
||||
/// </summary>
|
||||
/// <param name="DirectoryPath">Directory Path to open</param>
|
||||
/// <param name="FileName">Extra FileName Info</param>
|
||||
public void OpenDirectory(string DirectoryPath, string FileName = null);
|
||||
/// <param name="FileNameOrFilePath">Extra FileName Info</param>
|
||||
public void OpenDirectory(string DirectoryPath, string FileNameOrFilePath = null);
|
||||
|
||||
/// <summary>
|
||||
/// Opens the URL with the given Uri object.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Flow.Launcher.Plugin
|
||||
|
|
@ -37,7 +38,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// user's clipboard when Ctrl + C is pressed on a result. If the text is a file/directory path
|
||||
/// flow will copy the actual file/folder instead of just the path text.
|
||||
/// </summary>
|
||||
public string CopyText { get; set; } = string.Empty;
|
||||
public string CopyText
|
||||
{
|
||||
get => string.IsNullOrEmpty(_copyText) ? SubTitle : _copyText;
|
||||
set => _copyText = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This holds the text which can be provided by plugin to help Flow autocomplete text
|
||||
|
|
@ -56,9 +61,14 @@ namespace Flow.Launcher.Plugin
|
|||
get { return _icoPath; }
|
||||
set
|
||||
{
|
||||
if (!string.IsNullOrEmpty(PluginDirectory) && !Path.IsPathRooted(value))
|
||||
// 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))
|
||||
{
|
||||
_icoPath = Path.Combine(value, IcoPath);
|
||||
_icoPath = Path.Combine(PluginDirectory, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -66,6 +76,10 @@ namespace Flow.Launcher.Plugin
|
|||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Determines if Icon has a border radius
|
||||
/// </summary>
|
||||
public bool RoundedIcon { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Delegate function, see <see cref="Icon"/>
|
||||
|
|
@ -77,6 +91,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// Delegate to Get Image Source
|
||||
/// </summary>
|
||||
public IconDelegate Icon;
|
||||
private string _copyText = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Information for Glyph Icon (Prioritized than IcoPath/Icon if user enable Glyph Icons)
|
||||
|
|
@ -131,10 +146,11 @@ namespace Flow.Launcher.Plugin
|
|||
set
|
||||
{
|
||||
_pluginDirectory = value;
|
||||
if (!string.IsNullOrEmpty(IcoPath) && !Path.IsPathRooted(IcoPath))
|
||||
{
|
||||
IcoPath = Path.Combine(value, IcoPath);
|
||||
}
|
||||
|
||||
// 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.
|
||||
IcoPath = _icoPath;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -162,7 +178,7 @@ namespace Flow.Launcher.Plugin
|
|||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Title + SubTitle;
|
||||
return Title + SubTitle + Score;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -188,6 +204,11 @@ namespace Flow.Launcher.Plugin
|
|||
/// </summary>
|
||||
public string SubTitleToolTip { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Customized Preview Panel
|
||||
/// </summary>
|
||||
public Lazy<UserControl> PreviewPanel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Run this result, asynchronously
|
||||
/// </summary>
|
||||
|
|
@ -197,5 +218,43 @@ namespace Flow.Launcher.Plugin
|
|||
{
|
||||
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>
|
||||
public int? ProgressBar { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Optionally set the color of the progress bar
|
||||
/// </summary>
|
||||
/// <default>#26a0da (blue)</default>
|
||||
public string ProgressBarColor { get; set; } = "#26a0da";
|
||||
|
||||
public PreviewInfo Preview { get; set; } = PreviewInfo.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Info of the preview image.
|
||||
/// </summary>
|
||||
public record PreviewInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Full image used for preview panel
|
||||
/// </summary>
|
||||
public string PreviewImagePath { get; set; }
|
||||
/// <summary>
|
||||
/// Determines if the preview image should occupy the full width of the preview panel.
|
||||
/// </summary>
|
||||
public bool IsMedia { get; set; }
|
||||
public string Description { get; set; }
|
||||
public IconDelegate PreviewDelegate { get; set; }
|
||||
|
||||
public static PreviewInfo Default { get; } = new()
|
||||
{
|
||||
PreviewImagePath = null,
|
||||
Description = null,
|
||||
IsMedia = false,
|
||||
PreviewDelegate = null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
#pragma warning disable IDE0005
|
||||
using System.Windows;
|
||||
#pragma warning restore IDE0005
|
||||
|
||||
namespace Flow.Launcher.Plugin.SharedCommands
|
||||
{
|
||||
|
|
@ -12,8 +14,6 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
{
|
||||
private const string FileExplorerProgramName = "explorer";
|
||||
|
||||
private const string FileExplorerProgramEXE = "explorer.exe";
|
||||
|
||||
/// <summary>
|
||||
/// Copies the folder and all of its files and folders
|
||||
/// including subfolders to the target location
|
||||
|
|
@ -149,7 +149,12 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
/// <param name="fileOrFolderPath"></param>
|
||||
public static void OpenPath(string fileOrFolderPath)
|
||||
{
|
||||
var psi = new ProcessStartInfo { FileName = FileExplorerProgramName, UseShellExecute = true, Arguments = '"' + fileOrFolderPath + '"' };
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = FileExplorerProgramName,
|
||||
UseShellExecute = true,
|
||||
Arguments = '"' + fileOrFolderPath + '"'
|
||||
};
|
||||
try
|
||||
{
|
||||
if (LocationExists(fileOrFolderPath) || FileExists(fileOrFolderPath))
|
||||
|
|
@ -165,15 +170,6 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the folder that contains <paramref name="path"/>
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
public static void OpenContainingFolder(string path)
|
||||
{
|
||||
Process.Start(FileExplorerProgramEXE, $" /select,\"{path}\"");
|
||||
}
|
||||
|
||||
///<summary>
|
||||
/// This checks whether a given string is a directory path or network location string.
|
||||
/// It does not check if location actually exists.
|
||||
|
|
@ -206,22 +202,16 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
///</summary>
|
||||
public static string GetPreviousExistingDirectory(Func<string, bool> locationExists, string path)
|
||||
{
|
||||
var previousDirectoryPath = "";
|
||||
var index = path.LastIndexOf('\\');
|
||||
if (index > 0 && index < (path.Length - 1))
|
||||
{
|
||||
previousDirectoryPath = path.Substring(0, index + 1);
|
||||
if (!locationExists(previousDirectoryPath))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
string previousDirectoryPath = path.Substring(0, index + 1);
|
||||
return locationExists(previousDirectoryPath) ? previousDirectoryPath : "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return previousDirectoryPath;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
|
|
@ -241,5 +231,33 @@ namespace Flow.Launcher.Plugin.SharedCommands
|
|||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns if <paramref name="parentPath"/> contains <paramref name="subPath"/>.
|
||||
/// From https://stackoverflow.com/a/66877016
|
||||
/// </summary>
|
||||
/// <param name="parentPath">Parent path</param>
|
||||
/// <param name="subPath">Sub path</param>
|
||||
/// <param name="allowEqual">If <see langword="true"/>, when <paramref name="parentPath"/> and <paramref name="subPath"/> are equal, returns <see langword="true"/></param>
|
||||
/// <returns></returns>
|
||||
public static bool PathContains(string parentPath, string subPath, bool allowEqual = false)
|
||||
{
|
||||
var rel = Path.GetRelativePath(parentPath.EnsureTrailingSlash(), subPath);
|
||||
return (rel != "." || allowEqual)
|
||||
&& rel != ".."
|
||||
&& !rel.StartsWith("../")
|
||||
&& !rel.StartsWith(@"..\")
|
||||
&& !Path.IsPathRooted(rel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns path ended with "\"
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static string EnsureTrailingSlash(this string path)
|
||||
{
|
||||
return path.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
53
Flow.Launcher.Test/FilesFoldersTest.cs
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
using Flow.Launcher.Plugin.SharedCommands;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Flow.Launcher.Test
|
||||
{
|
||||
[TestFixture]
|
||||
|
||||
public class FilesFoldersTest
|
||||
{
|
||||
// Testcases from https://stackoverflow.com/a/31941905/20703207
|
||||
// Disk
|
||||
[TestCase(@"c:", @"c:\foo", true)]
|
||||
[TestCase(@"c:\", @"c:\foo", true)]
|
||||
// Slash
|
||||
[TestCase(@"c:\foo\bar\", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foo\bar", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\bar", true)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo\bar", true)]
|
||||
// File
|
||||
[TestCase(@"c:\foo", @"c:\foo\a.txt", true)]
|
||||
[TestCase(@"c:\foo", @"c:/foo/a.txt", true)]
|
||||
[TestCase(@"c:\FOO\a.txt", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foo\a.txt", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foobar\a.txt", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foobar\a.txt", @"c:\foo\", false)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo.txt", false)]
|
||||
// Prefix
|
||||
[TestCase(@"c:\foo", @"c:\foobar", false)]
|
||||
[TestCase(@"C:\Program", @"C:\Program Files\", false)]
|
||||
[TestCase(@"c:\foobar", @"c:\foo\a.txt", false)]
|
||||
[TestCase(@"c:\foobar\", @"c:\foo\a.txt", false)]
|
||||
// Edge case
|
||||
[TestCase(@"c:\foo", @"c:\foo\..\bar\baz", false)]
|
||||
[TestCase(@"c:\bar", @"c:\foo\..\bar\baz", true)]
|
||||
[TestCase(@"c:\barr", @"c:\foo\..\bar\baz", false)]
|
||||
// Equality
|
||||
[TestCase(@"c:\foo", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo", false)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\", false)]
|
||||
public void GivenTwoPaths_WhenCheckPathContains_ThenShouldBeExpectedResult(string parentPath, string path, bool expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path));
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo", @"c:\foo", true)]
|
||||
[TestCase(@"c:\foo\", @"c:\foo", true)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\", true)]
|
||||
public void GivenTwoPathsAreTheSame_WhenCheckPathContains_ThenShouldBeTrue(string parentPath, string path, bool expectedResult)
|
||||
{
|
||||
Assert.AreEqual(expectedResult, FilesFolders.PathContains(parentPath, path, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
<ProjectGuid>{FF742965-9A80-41A5-B042-D6C7D3A21708}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
|
|
@ -48,13 +48,13 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0">
|
||||
<PackageReference Include="Moq" Version="4.18.4" />
|
||||
<PackageReference Include="nunit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
|
@ -129,14 +129,20 @@ namespace Flow.Launcher.Test
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// These are standard match scenarios
|
||||
/// The intention of this test is provide a bench mark for how much the score has increased from a change.
|
||||
/// Usually the increase in scoring should not be drastic, increase of less than 10 is acceptable.
|
||||
/// </summary>
|
||||
[TestCase(Chrome, Chrome, 157)]
|
||||
[TestCase(Chrome, LastIsChrome, 147)]
|
||||
[TestCase(Chrome, LastIsChrome, 145)]
|
||||
[TestCase("chro", HelpCureHopeRaiseOnMindEntityChrome, 50)]
|
||||
[TestCase("chr", HelpCureHopeRaiseOnMindEntityChrome, 30)]
|
||||
[TestCase(Chrome, UninstallOrChangeProgramsOnYourComputer, 21)]
|
||||
[TestCase(Chrome, CandyCrushSagaFromKing, 0)]
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, 110)]
|
||||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 121)] //double spacing intended
|
||||
[TestCase("sql", MicrosoftSqlServerManagementStudio, 109)]
|
||||
[TestCase("sql manag", MicrosoftSqlServerManagementStudio, 120)] //double spacing intended
|
||||
public void WhenGivenQueryString_ThenShouldReturn_TheDesiredScoring(
|
||||
string queryString, string compareString, int expectedScore)
|
||||
{
|
||||
|
|
@ -275,7 +281,40 @@ namespace Flow.Launcher.Test
|
|||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString1Result.Score}{Environment.NewLine}");
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("red", "red colour", "metro red")]
|
||||
[TestCase("red", "this red colour", "this colour red")]
|
||||
[TestCase("red", "this red colour", "this colour is very red")]
|
||||
[TestCase("red", "this red colour", "this colour is surprisingly super awesome red and cool")]
|
||||
[TestCase("red", "this colour is surprisingly super red very and cool", "this colour is surprisingly super very red and cool")]
|
||||
public void WhenGivenTwoStrings_Scoring_ShouldGiveMoreWeightToTheStringCloserToIndexZero(
|
||||
string queryString, string compareString1, string compareString2)
|
||||
{
|
||||
// When
|
||||
var matcher = new StringMatcher { UserSettingSearchPrecision = SearchPrecisionScore.Regular };
|
||||
|
||||
// Given
|
||||
var compareString1Result = matcher.FuzzyMatch(queryString, compareString1);
|
||||
var compareString2Result = matcher.FuzzyMatch(queryString, compareString2);
|
||||
|
||||
Debug.WriteLine("");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine($"QueryString: \"{queryString}\"{Environment.NewLine}");
|
||||
Debug.WriteLine(
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}");
|
||||
Debug.WriteLine(
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
Debug.WriteLine("###############################################");
|
||||
Debug.WriteLine("");
|
||||
|
||||
// Should
|
||||
Assert.True(compareString1Result.Score > compareString2Result.Score,
|
||||
$"Query: \"{queryString}\"{Environment.NewLine} " +
|
||||
$"CompareString1: \"{compareString1}\", Score: {compareString1Result.Score}{Environment.NewLine}" +
|
||||
$"Should be greater than{Environment.NewLine}" +
|
||||
$"CompareString2: \"{compareString2}\", Score: {compareString2Result.Score}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("vim", "Vim", "ignoreDescription", "ignore.exe", "Vim Diff", "ignoreDescription", "ignore.exe")]
|
||||
|
|
|
|||
|
|
@ -7,8 +7,11 @@ using Flow.Launcher.Plugin.SharedCommands;
|
|||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using static Flow.Launcher.Plugin.Explorer.Search.SearchManager;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
{
|
||||
|
|
@ -32,12 +35,11 @@ namespace Flow.Launcher.Test.Plugins
|
|||
{
|
||||
new Result
|
||||
{
|
||||
Title="Result 1"
|
||||
Title = "Result 1"
|
||||
},
|
||||
|
||||
new Result
|
||||
{
|
||||
Title="Result 2"
|
||||
Title = "Result 2"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -46,15 +48,13 @@ namespace Flow.Launcher.Test.Plugins
|
|||
|
||||
private bool PreviousLocationNotExistReturnsFalse(string dummyString) => false;
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("C:\\SomeFolder\\", "directory='file:C:\\SomeFolder\\'")]
|
||||
public void GivenWindowsIndexSearch_WhenProvidedFolderPath_ThenQueryWhereRestrictionsShouldUseDirectoryString(string path, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
// When
|
||||
var folderPath = path;
|
||||
var result = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(folderPath);
|
||||
var result = QueryConstructor.TopLevelDirectoryConstraint(folderPath);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(result == expectedString,
|
||||
|
|
@ -62,6 +62,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual: {result}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[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")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchTypeIsTopLevelDirectorySearch_ThenQueryShouldUseExpectedString(string folderPath, string expectedString)
|
||||
|
|
@ -70,130 +71,68 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(folderPath);
|
||||
var queryString = queryConstructor.Directory(folderPath);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString == expectedString,
|
||||
Assert.IsTrue(queryString.Replace(" ", " ") == expectedString.Replace(" ", " "),
|
||||
$"Expected string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {queryString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("C:\\SomeFolder\\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 directory='file:C:\\SomeFolder' ORDER BY System.FileName")]
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[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")]
|
||||
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
string folderPath, string userSearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var queryString = queryConstructor.QueryForTopLevelDirectorySearch(userSearchString);
|
||||
var queryString = queryConstructor.Directory(folderPath, userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString == expectedString,
|
||||
$"Expected string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {queryString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("C:\\SomeFolder\\SomeApp", "(System.FileName LIKE 'SomeApp%' " +
|
||||
"OR CONTAINS(System.FileName,'\"SomeApp*\"',1033))" +
|
||||
" AND directory='file:C:\\SomeFolder'")]
|
||||
public void GivenWindowsIndexSearchTopLevelDirectory_WhenSearchingForSpecificItem_ThenQueryWhereRestrictionsShouldUseDirectoryString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var queryString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectorySearch(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(queryString == expectedString,
|
||||
$"Expected string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {queryString}{Environment.NewLine}");
|
||||
Assert.AreEqual(expectedString, queryString);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("scope='file:'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryWhereRestrictionsShouldUseScopeString(string expectedString)
|
||||
{
|
||||
//When
|
||||
var resultString = QueryConstructor.QueryWhereRestrictionsForAllFilesAndFoldersSearch;
|
||||
const string resultString = QueryConstructor.RestrictionsForAllFilesAndFoldersSearch;
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[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")]
|
||||
"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")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchAllFoldersAndFiles_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
var baseQuery = queryConstructor.CreateBaseQuery();
|
||||
|
||||
var baseQuery = queryConstructor.CreateBaseQuery();
|
||||
|
||||
// system running this test could have different locale than the hard-coded 1033 LCID en-US.
|
||||
var queryKeywordLocale = baseQuery.QueryKeywordLocale;
|
||||
expectedString = expectedString.Replace("1033", queryKeywordLocale.ToString());
|
||||
|
||||
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryForAllFilesAndFolders(userSearchString);
|
||||
var resultString = queryConstructor.FilesAndFolders(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
$"Expected query string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldContinueDirectoryInfoClassSearchAsync()
|
||||
{
|
||||
// Given
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
// When
|
||||
var results = await searchManager.TopLevelDirectorySearchBehaviourAsync(
|
||||
MethodWindowsIndexSearchReturnsZeroResultsAsync,
|
||||
MethodDirectoryInfoClassSearchReturnsTwoResults,
|
||||
false,
|
||||
new Query(),
|
||||
"string not used",
|
||||
default);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(results.Count == 2,
|
||||
$"Expected to have 2 results from DirectoryInfoClassSearch {Environment.NewLine} " +
|
||||
$"Actual number of results is {results.Count} {Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public async Task GivenTopLevelDirectorySearch_WhenIndexSearchNotRequired_ThenSearchMethodShouldNotContinueDirectoryInfoClassSearchAsync()
|
||||
{
|
||||
// Given
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
// When
|
||||
var results = await searchManager.TopLevelDirectorySearchBehaviourAsync(
|
||||
MethodWindowsIndexSearchReturnsZeroResultsAsync,
|
||||
MethodDirectoryInfoClassSearchReturnsTwoResults,
|
||||
true,
|
||||
new Query(),
|
||||
"string not used",
|
||||
default);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(results.Count == 0,
|
||||
$"Expected to have 0 results because location is indexed {Environment.NewLine} " +
|
||||
$"Actual number of results is {results.Count} {Environment.NewLine}");
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase(@"some words", @"FREETEXT('some words')")]
|
||||
public void GivenWindowsIndexSearch_WhenQueryWhereRestrictionsIsForFileContentSearch_ThenShouldReturnFreeTextString(
|
||||
string querySearchString, string expectedString)
|
||||
|
|
@ -202,7 +141,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryWhereRestrictionsForFileContentSearch(querySearchString);
|
||||
var resultString = QueryConstructor.RestrictionsForFileContentSearch(querySearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
|
|
@ -210,8 +149,9 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
[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 System.FileName")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchForFileContent_ThenQueryShouldUseExpectedString(
|
||||
string userSearchString, string expectedString)
|
||||
{
|
||||
|
|
@ -219,7 +159,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryForFileContentSearch(userSearchString);
|
||||
var resultString = queryConstructor.FileContent(userSearchString);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
|
|
@ -230,7 +170,10 @@ namespace Flow.Launcher.Test.Plugins
|
|||
public void GivenQuery_WhenActionKeywordForFileContentSearchExists_ThenFileContentSearchRequiredShouldReturnTrue()
|
||||
{
|
||||
// Given
|
||||
var query = new Query { ActionKeyword = "doc:", Search = "search term" };
|
||||
var query = new Query
|
||||
{
|
||||
ActionKeyword = "doc:", Search = "search term"
|
||||
};
|
||||
|
||||
var searchManager = new SearchManager(new Settings(), new PluginInitContext());
|
||||
|
||||
|
|
@ -252,6 +195,7 @@ namespace Flow.Launcher.Test.Plugins
|
|||
[TestCase(@"c:\>*", true)]
|
||||
[TestCase(@"c:\>", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation\>", true)]
|
||||
[TestCase(@"c:\SomeLocation\SomeOtherLocation", true)]
|
||||
public void WhenGivenQuerySearchString_ThenShouldIndicateIfIsLocationPathString(string querySearchString, bool expectedResult)
|
||||
{
|
||||
// When, Given
|
||||
|
|
@ -303,24 +247,19 @@ namespace Flow.Launcher.Test.Plugins
|
|||
$"Actual path string is {returnedPath} {Environment.NewLine}");
|
||||
}
|
||||
|
||||
[TestCase("c:\\SomeFolder\\>", "scope='file:c:\\SomeFolder'")]
|
||||
[TestCase("c:\\SomeFolder\\>SomeName", "(System.FileName LIKE 'SomeName%' "
|
||||
+ "OR CONTAINS(System.FileName,'\"SomeName*\"',1033)) AND "
|
||||
+ "scope='file:c:\\SomeFolder'")]
|
||||
public void GivenWindowsIndexSearch_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString)
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("c:\\SomeFolder", "scope='file:c:\\SomeFolder'")]
|
||||
[TestCase("c:\\OtherFolder", "scope='file:c:\\OtherFolder'")]
|
||||
public void GivenFilePath_WhenSearchPatternHotKeyIsSearchAll_ThenQueryWhereRestrictionsShouldUseScopeString(string path, string expectedString)
|
||||
{
|
||||
// Given
|
||||
var queryConstructor = new QueryConstructor(new Settings());
|
||||
|
||||
//When
|
||||
var resultString = queryConstructor.QueryWhereRestrictionsForTopLevelDirectoryAllFilesAndFoldersSearch(path);
|
||||
var resultString = QueryConstructor.RecursiveDirectoryConstraint(path);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
$"Expected QueryWhereRestrictions string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual string was: {resultString}{Environment.NewLine}");
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("windows7.0")]
|
||||
[TestCase("c:\\somefolder\\>somefile", "*somefile*")]
|
||||
[TestCase("c:\\somefolder\\somefile", "somefile*")]
|
||||
[TestCase("c:\\somefolder\\", "*")]
|
||||
|
|
@ -331,9 +270,194 @@ namespace Flow.Launcher.Test.Plugins
|
|||
var resultString = DirectoryInfoSearch.ConstructSearchCriteria(path);
|
||||
|
||||
// Then
|
||||
Assert.IsTrue(resultString == expectedString,
|
||||
$"Expected criteria string: {expectedString}{Environment.NewLine} " +
|
||||
$"Actual criteria string was: {resultString}{Environment.NewLine}");
|
||||
Assert.AreEqual(expectedString, resultString);
|
||||
}
|
||||
|
||||
[TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", false, true, "c:\\somefolder\\someotherfolder\\")]
|
||||
[TestCase("c:\\somefolder\\someotherfolder\\", ResultType.Folder, "irrelevant", true, true, "c:\\somefolder\\someotherfolder\\")]
|
||||
[TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "irrelevant", true, false, "p c:\\somefolder\\someotherfolder\\")]
|
||||
[TestCase("c:\\somefolder\\someotherfolder\\", ResultType.Folder, "irrelevant", false, false, "c:\\somefolder\\someotherfolder\\")]
|
||||
[TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "p", true, false, "p c:\\somefolder\\someotherfolder\\")]
|
||||
[TestCase("c:\\somefolder\\someotherfolder", ResultType.Folder, "", true, true, "c:\\somefolder\\someotherfolder\\")]
|
||||
public void GivenFolderResult_WhenGetPath_ThenPathShouldBeExpectedString(
|
||||
string path,
|
||||
ResultType type,
|
||||
string actionKeyword,
|
||||
bool pathSearchKeywordEnabled,
|
||||
bool searchActionKeywordEnabled,
|
||||
string expectedResult)
|
||||
{
|
||||
// Given
|
||||
var settings = new Settings()
|
||||
{
|
||||
PathSearchKeywordEnabled = pathSearchKeywordEnabled,
|
||||
PathSearchActionKeyword = "p",
|
||||
SearchActionKeywordEnabled = searchActionKeywordEnabled,
|
||||
SearchActionKeyword = Query.GlobalPluginWildcardSign
|
||||
};
|
||||
ResultManager.Init(new PluginInitContext(), settings);
|
||||
|
||||
// When
|
||||
var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, true, "e c:\\somefolder\\somefile")]
|
||||
[TestCase("c:\\somefolder\\somefile", ResultType.File, "p", true, false, "p c:\\somefolder\\somefile")]
|
||||
[TestCase("c:\\somefolder\\somefile", ResultType.File, "e", true, true, "e c:\\somefolder\\somefile")]
|
||||
[TestCase("c:\\somefolder\\somefile", ResultType.File, "irrelevant", false, false, "e c:\\somefolder\\somefile")]
|
||||
public void GivenFileResult_WhenGetPath_ThenPathShouldBeExpectedString(
|
||||
string path,
|
||||
ResultType type,
|
||||
string actionKeyword,
|
||||
bool pathSearchKeywordEnabled,
|
||||
bool searchActionKeywordEnabled,
|
||||
string expectedResult)
|
||||
{
|
||||
// Given
|
||||
var settings = new Settings()
|
||||
{
|
||||
PathSearchKeywordEnabled = pathSearchKeywordEnabled,
|
||||
PathSearchActionKeyword = "p",
|
||||
SearchActionKeywordEnabled = searchActionKeywordEnabled,
|
||||
SearchActionKeyword = "e"
|
||||
};
|
||||
ResultManager.Init(new PluginInitContext(), settings);
|
||||
|
||||
// When
|
||||
var result = ResultManager.GetPathWithActionKeyword(path, type, actionKeyword);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "q", false, false, "q somefolder")]
|
||||
[TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "i", true, false, "p c:\\somefolder\\")]
|
||||
[TestCase("somefolder", "c:\\somefolder\\", ResultType.Folder, "irrelevant", true, true, "c:\\somefolder\\")]
|
||||
public void GivenQueryWithFolderTypeResult_WhenGetAutoComplete_ThenResultShouldBeExpectedString(
|
||||
string title,
|
||||
string path,
|
||||
ResultType resultType,
|
||||
string actionKeyword,
|
||||
bool pathSearchKeywordEnabled,
|
||||
bool searchActionKeywordEnabled,
|
||||
string expectedResult)
|
||||
{
|
||||
// Given
|
||||
var query = new Query() { ActionKeyword = actionKeyword };
|
||||
var settings = new Settings()
|
||||
{
|
||||
PathSearchKeywordEnabled = pathSearchKeywordEnabled,
|
||||
PathSearchActionKeyword = "p",
|
||||
SearchActionKeywordEnabled = searchActionKeywordEnabled,
|
||||
SearchActionKeyword = Query.GlobalPluginWildcardSign,
|
||||
QuickAccessActionKeyword = "q",
|
||||
IndexSearchActionKeyword = "i"
|
||||
};
|
||||
ResultManager.Init(new PluginInitContext(), settings);
|
||||
|
||||
// When
|
||||
var result = ResultManager.GetAutoCompleteText(title, query, path, resultType);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "q", false, false, "q somefile")]
|
||||
[TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "i", true, false, "p c:\\somefolder\\somefile")]
|
||||
[TestCase("somefile", "c:\\somefolder\\somefile", ResultType.File, "irrelevant", true, true, "c:\\somefolder\\somefile")]
|
||||
public void GivenQueryWithFileTypeResult_WhenGetAutoComplete_ThenResultShouldBeExpectedString(
|
||||
string title,
|
||||
string path,
|
||||
ResultType resultType,
|
||||
string actionKeyword,
|
||||
bool pathSearchKeywordEnabled,
|
||||
bool searchActionKeywordEnabled,
|
||||
string expectedResult)
|
||||
{
|
||||
// Given
|
||||
var query = new Query() { ActionKeyword = actionKeyword };
|
||||
var settings = new Settings()
|
||||
{
|
||||
QuickAccessActionKeyword = "q",
|
||||
IndexSearchActionKeyword = "i",
|
||||
PathSearchActionKeyword = "p",
|
||||
PathSearchKeywordEnabled = pathSearchKeywordEnabled,
|
||||
SearchActionKeywordEnabled = searchActionKeywordEnabled,
|
||||
SearchActionKeyword = Query.GlobalPluginWildcardSign
|
||||
};
|
||||
ResultManager.Init(new PluginInitContext(), settings);
|
||||
|
||||
// When
|
||||
var result = ResultManager.GetAutoCompleteText(title, query, path, resultType);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo", @"c:\foo", true)]
|
||||
[TestCase(@"C:\Foo\", @"c:\foo\", true)]
|
||||
[TestCase(@"c:\foo", @"c:\foo\", false)]
|
||||
public void GivenTwoPaths_WhenCompared_ThenShouldBeExpectedSameOrDifferent(string path1, string path2, bool expectedResult)
|
||||
{
|
||||
// Given
|
||||
var comparator = PathEqualityComparator.Instance;
|
||||
var result1 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path1),
|
||||
SubTitle = path1
|
||||
};
|
||||
var result2 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path2),
|
||||
SubTitle = path2
|
||||
};
|
||||
|
||||
// When, Then
|
||||
Assert.AreEqual(expectedResult, comparator.Equals(result1, result2));
|
||||
}
|
||||
|
||||
[TestCase(@"c:\foo\", @"c:\foo\")]
|
||||
[TestCase(@"C:\Foo\", @"c:\foo\")]
|
||||
public void GivenTwoPaths_WhenComparedHasCode_ThenShouldBeSame(string path1, string path2)
|
||||
{
|
||||
// Given
|
||||
var comparator = PathEqualityComparator.Instance;
|
||||
var result1 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path1),
|
||||
SubTitle = path1
|
||||
};
|
||||
var result2 = new Result
|
||||
{
|
||||
Title = Path.GetFileName(path2),
|
||||
SubTitle = path2
|
||||
};
|
||||
|
||||
var hash1 = comparator.GetHashCode(result1);
|
||||
var hash2 = comparator.GetHashCode(result2);
|
||||
|
||||
// When, Then
|
||||
Assert.IsTrue(hash1 == hash2);
|
||||
}
|
||||
|
||||
[TestCase(@"%appdata%", true)]
|
||||
[TestCase(@"%appdata%\123", true)]
|
||||
[TestCase(@"c:\foo %appdata%\", false)]
|
||||
[TestCase(@"c:\users\%USERNAME%\downloads", true)]
|
||||
[TestCase(@"c:\downloads", false)]
|
||||
[TestCase(@"%", false)]
|
||||
[TestCase(@"%%", false)]
|
||||
[TestCase(@"%bla%blabla%", false)]
|
||||
public void GivenPath_WhenHavingEnvironmentVariableOrNot_ThenShouldBeExpected(string path, bool expectedResult)
|
||||
{
|
||||
// When
|
||||
var result = EnvironmentVariables.HasEnvironmentVar(path);
|
||||
|
||||
// Then
|
||||
Assert.AreEqual(result, expectedResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using NUnit;
|
||||
using NUnit;
|
||||
using NUnit.Framework;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Plugin;
|
||||
|
|
@ -16,8 +16,6 @@ namespace Flow.Launcher.Test.Plugins
|
|||
// ReSharper disable once InconsistentNaming
|
||||
internal class JsonRPCPluginTest : JsonRPCPlugin
|
||||
{
|
||||
public override string SupportedLanguage { get; set; } = AllowedLanguage.Executable;
|
||||
|
||||
protected override string Request(JsonRPCRequestModel rpcRequest, CancellationToken token = default)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
using Flow.Launcher.Plugin.Program.Programs;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using Windows.ApplicationModel;
|
||||
|
||||
namespace Flow.Launcher.Test.Plugins
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProgramTest
|
||||
{
|
||||
[TestCase("Microsoft.WindowsCamera", "ms-resource:LensSDK/Resources/AppTitle", "ms-resource://Microsoft.WindowsCamera/LensSDK/Resources/AppTitle")]
|
||||
[TestCase("microsoft.windowscommunicationsapps", "ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName",
|
||||
"ms-resource://microsoft.windowscommunicationsapps/hxoutlookintl/AppManifest_MailDesktop_DisplayName")]
|
||||
[TestCase("windows.immersivecontrolpanel", "ms-resource:DisplayName", "ms-resource://windows.immersivecontrolpanel/Resources/DisplayName")]
|
||||
[TestCase("Microsoft.MSPaint", "ms-resource:AppName", "ms-resource://Microsoft.MSPaint/Resources/AppName")]
|
||||
public void WhenGivenPriReferenceValueShouldReturnCorrectFormat(string packageName, string rawPriReferenceValue, string expectedFormat)
|
||||
{
|
||||
// Arrange
|
||||
var app = new UWP.Application();
|
||||
|
||||
// Act
|
||||
var result = app.FormattedPriReferenceValue(packageName, rawPriReferenceValue);
|
||||
|
||||
// Assert
|
||||
Assert.IsTrue(result == expectedFormat,
|
||||
$"Expected Pri reference format: {expectedFormat}{Environment.NewLine} " +
|
||||
$"Actual: {result}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29806.167
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.3.32901.215
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flow.Launcher.Test", "Flow.Launcher.Test\Flow.Launcher.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
|
|
@ -80,7 +80,7 @@ Global
|
|||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{FF742965-9A80-41A5-B042-D6C7D3A21708}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@
|
|||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
FontFamily="Segoe UI"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource actionKeywordsTitle}"
|
||||
|
|
|
|||
|
|
@ -8,24 +8,17 @@ using Flow.Launcher.ViewModel;
|
|||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class ActionKeywords : Window
|
||||
public partial class ActionKeywords
|
||||
{
|
||||
private readonly PluginPair plugin;
|
||||
private Settings settings;
|
||||
private readonly Internationalization translater = InternationalizationManager.Instance;
|
||||
private readonly PluginViewModel pluginViewModel;
|
||||
|
||||
public ActionKeywords(string pluginId, Settings settings, PluginViewModel pluginViewModel)
|
||||
public ActionKeywords(PluginViewModel pluginViewModel)
|
||||
{
|
||||
InitializeComponent();
|
||||
plugin = PluginManager.GetPluginForId(pluginId);
|
||||
this.settings = settings;
|
||||
plugin = pluginViewModel.PluginPair;
|
||||
this.pluginViewModel = pluginViewModel;
|
||||
if (plugin == null)
|
||||
{
|
||||
MessageBox.Show(translater.GetTranslation("cannotFindSpecifiedPlugin"));
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using System.Windows;
|
||||
using Flow.Launcher.Core;
|
||||
using Flow.Launcher.Core.Configuration;
|
||||
using Flow.Launcher.Core.ExternalPlugins.Environments;
|
||||
using Flow.Launcher.Core.Plugin;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.Helper;
|
||||
|
|
@ -61,6 +63,8 @@ namespace Flow.Launcher
|
|||
_settingsVM = new SettingWindowViewModel(_updater, _portable);
|
||||
_settings = _settingsVM.Settings;
|
||||
|
||||
AbstractPluginEnvironment.PreStartPluginExecutablePathUpdate(_settings);
|
||||
|
||||
_alphabet.Initialize(_settings);
|
||||
_stringMatcher = new StringMatcher(_alphabet);
|
||||
StringMatcher.Instance = _stringMatcher;
|
||||
|
|
@ -81,14 +85,14 @@ namespace Flow.Launcher
|
|||
|
||||
Current.MainWindow = window;
|
||||
Current.MainWindow.Title = Constant.FlowLauncher;
|
||||
|
||||
|
||||
HotKeyMapper.Initialize(_mainVM);
|
||||
|
||||
// happlebao todo temp fix for instance code logic
|
||||
// todo temp fix for instance code logic
|
||||
// load plugin before change language, because plugin language also needs be changed
|
||||
InternationalizationManager.Instance.Settings = _settings;
|
||||
InternationalizationManager.Instance.ChangeLanguage(_settings.Language);
|
||||
// main windows needs initialized before theme change because of blur settigns
|
||||
// main windows needs initialized before theme change because of blur settings
|
||||
ThemeManager.Instance.Settings = _settings;
|
||||
ThemeManager.Instance.ChangeTheme(_settings.Theme);
|
||||
|
||||
|
|
@ -127,20 +131,17 @@ namespace Flow.Launcher
|
|||
//[Conditional("RELEASE")]
|
||||
private void AutoUpdates()
|
||||
{
|
||||
Task.Run(async () =>
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
if (_settings.AutoUpdates)
|
||||
{
|
||||
// check udpate every 5 hours
|
||||
var timer = new Timer(1000 * 60 * 60 * 5);
|
||||
timer.Elapsed += async (s, e) =>
|
||||
{
|
||||
await _updater.UpdateAppAsync(API);
|
||||
};
|
||||
timer.Start();
|
||||
|
||||
// check updates on startup
|
||||
// check update every 5 hours
|
||||
var timer = new PeriodicTimer(TimeSpan.FromHours(5));
|
||||
await _updater.UpdateAppAsync(API);
|
||||
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
// check updates on startup
|
||||
await _updater.UpdateAppAsync(API);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -183,7 +184,7 @@ namespace Flow.Launcher
|
|||
|
||||
public void OnSecondAppStarted()
|
||||
{
|
||||
Current.MainWindow.Show();
|
||||
_mainVM.Show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
55
Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
internal class BoolToIMEConversionModeConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool v)
|
||||
{
|
||||
if (v)
|
||||
{
|
||||
return ImeConversionModeValues.Alphanumeric;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ImeConversionModeValues.DoNotCare;
|
||||
}
|
||||
}
|
||||
return ImeConversionModeValues.DoNotCare;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
internal class BoolToIMEStateConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is bool v)
|
||||
{
|
||||
if (v)
|
||||
{
|
||||
return InputMethodState.Off;
|
||||
}
|
||||
else
|
||||
{
|
||||
return InputMethodState.DoNotCare;
|
||||
}
|
||||
}
|
||||
return InputMethodState.DoNotCare;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
43
Flow.Launcher/Converters/BoolToVisibilityConverter.cs
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
public class BoolToVisibilityConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (parameter != null)
|
||||
{
|
||||
if (value is true)
|
||||
{
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (value is true)
|
||||
{
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
else {
|
||||
return Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
|
||||
}
|
||||
}
|
||||
19
Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
public class DateTimeFormatToNowConverter : IValueConverter
|
||||
{
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value is not string format ? null : DateTime.Now.ToString(format);
|
||||
}
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Flow.Launcher/Converters/DiameterToCenterPointConverter.cs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
public class DiameterToCenterPointConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is double d)
|
||||
{
|
||||
return new Point(d / 2, d / 2);
|
||||
}
|
||||
|
||||
return new Point(0, 0);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Flow.Launcher/Converters/IconRadiusConverter.cs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using Windows.Devices.PointOfService;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
public class IconRadiusConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values.Length != 2)
|
||||
throw new ArgumentException("IconRadiusConverter must have 2 parameters");
|
||||
|
||||
return values[1] switch
|
||||
{
|
||||
true => (double)values[0] / 2,
|
||||
false => (double)values[0],
|
||||
_ => throw new ArgumentException("The second argument should be boolean", nameof(values))
|
||||
};
|
||||
}
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,17 +11,17 @@ namespace Flow.Launcher.Converters
|
|||
[ValueConversion(typeof(bool), typeof(Visibility))]
|
||||
public class OpenResultHotkeyVisibilityConverter : IValueConverter
|
||||
{
|
||||
private const int MaxVisibleHotkeys = 9;
|
||||
private const int MaxVisibleHotkeys = 10;
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var hotkeyNumber = int.MaxValue;
|
||||
var number = int.MaxValue;
|
||||
|
||||
if (value is ListBoxItem listBoxItem
|
||||
&& ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
|
||||
hotkeyNumber = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
|
||||
number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
|
||||
|
||||
return hotkeyNumber <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
|
||||
return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
|
|
@ -10,7 +10,10 @@ namespace Flow.Launcher.Converters
|
|||
{
|
||||
if (value is ListBoxItem listBoxItem
|
||||
&& ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox)
|
||||
return listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
|
||||
{
|
||||
var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1;
|
||||
return res == 10 ? 0 : res; // 10th item => HOTKEY+0
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ namespace Flow.Launcher.Converters
|
|||
|
||||
// Check if Text will be larger then our QueryTextBox
|
||||
System.Windows.Media.Typeface typeface = new Typeface(QueryTextBox.FontFamily, QueryTextBox.FontStyle, QueryTextBox.FontWeight, QueryTextBox.FontStretch);
|
||||
System.Windows.Media.FormattedText ft = new FormattedText(QueryTextBox.Text, System.Globalization.CultureInfo.CurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, QueryTextBox.FontSize, Brushes.Black);
|
||||
// TODO: Obsolete warning?
|
||||
System.Windows.Media.FormattedText ft = new FormattedText(QueryTextBox.Text, System.Globalization.CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, QueryTextBox.FontSize, Brushes.Black);
|
||||
|
||||
var offset = QueryTextBox.Padding.Right;
|
||||
|
||||
|
|
@ -75,4 +76,4 @@ namespace Flow.Launcher.Converters
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
Flow.Launcher/Converters/StringToKeyBindingConverter.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
class StringToKeyBindingConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var mode = parameter as string;
|
||||
var hotkeyStr = value as string;
|
||||
var converter = new KeyGestureConverter();
|
||||
var key = (KeyGesture)converter.ConvertFromString(hotkeyStr);
|
||||
if (mode == "key")
|
||||
{
|
||||
return key.Key;
|
||||
}
|
||||
else if (mode == "modifiers")
|
||||
{
|
||||
return key.Modifiers;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
32
Flow.Launcher/Converters/TextConverter.cs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.ViewModel;
|
||||
|
||||
namespace Flow.Launcher.Converters
|
||||
{
|
||||
public class TextConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
var ID = value.ToString();
|
||||
switch(ID)
|
||||
{
|
||||
case PluginStoreItemViewModel.NewRelease:
|
||||
return InternationalizationManager.Instance.GetTranslation("pluginStore_NewRelease");
|
||||
case PluginStoreItemViewModel.RecentlyUpdated:
|
||||
return InternationalizationManager.Instance.GetTranslation("pluginStore_RecentlyUpdated");
|
||||
case PluginStoreItemViewModel.None:
|
||||
return InternationalizationManager.Instance.GetTranslation("pluginStore_None");
|
||||
case PluginStoreItemViewModel.Installed:
|
||||
return InternationalizationManager.Instance.GetTranslation("pluginStore_Installed");
|
||||
default:
|
||||
return ID;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
|
@ -64,7 +64,6 @@
|
|||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
FontFamily="Segoe UI"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource customeQueryHotkeyTitle}"
|
||||
|
|
@ -76,10 +75,14 @@
|
|||
Text="{DynamicResource customeQueryHotkeyTips}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<Image
|
||||
Width="478"
|
||||
Margin="0,20,0,0"
|
||||
Source="/Images/illustration_01.png" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,20,0,0" Orientation="Horizontal">
|
||||
<Grid Width="470">
|
||||
<Grid Width="478">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
|
|
@ -132,6 +135,7 @@
|
|||
LastChildFill="True">
|
||||
<Button
|
||||
x:Name="btnTestActionKeyword"
|
||||
Margin="0,0,10,0"
|
||||
Padding="10,5,10,5"
|
||||
Click="BtnTestActionKeyword_OnClick"
|
||||
Content="{DynamicResource preview}"
|
||||
|
|
|
|||
164
Flow.Launcher/CustomShortcutSetting.xaml
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
<Window
|
||||
x:Class="Flow.Launcher.CustomShortcutSetting"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:flowlauncher="clr-namespace:Flow.Launcher"
|
||||
Title="{DynamicResource customeQueryShortcutTitle}"
|
||||
Width="530"
|
||||
Background="{DynamicResource PopuBGColor}"
|
||||
DataContext="{Binding RelativeSource={RelativeSource Self}}"
|
||||
Foreground="{DynamicResource PopupTextColor}"
|
||||
Icon="Images\app.png"
|
||||
ResizeMode="NoResize"
|
||||
SizeToContent="Height"
|
||||
WindowStartupLocation="CenterScreen">
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="32" ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
|
||||
</WindowChrome.WindowChrome>
|
||||
<Window.InputBindings>
|
||||
<KeyBinding Key="Escape" Command="Close" />
|
||||
</Window.InputBindings>
|
||||
<Window.CommandBindings>
|
||||
<CommandBinding Command="Close" Executed="cmdEsc_OnPress" />
|
||||
</Window.CommandBindings>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition Height="80" />
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<StackPanel>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Button
|
||||
Grid.Column="4"
|
||||
Click="BtnCancel_OnClick"
|
||||
Style="{StaticResource TitleBarCloseButtonStyle}">
|
||||
<Path
|
||||
Width="46"
|
||||
Height="32"
|
||||
Data="M 18,11 27,20 M 18,20 27,11"
|
||||
Stroke="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type Button}}}"
|
||||
StrokeThickness="1">
|
||||
<Path.Style>
|
||||
<Style TargetType="Path">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding Path=IsActive, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}" Value="False">
|
||||
<Setter Property="Opacity" Value="0.5" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Path.Style>
|
||||
</Path>
|
||||
</Button>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
<StackPanel Margin="26,0,26,0">
|
||||
<StackPanel Grid.Row="0" Margin="0,0,0,12">
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
Margin="0,0,0,0"
|
||||
FontSize="20"
|
||||
FontWeight="SemiBold"
|
||||
Text="{DynamicResource customQueryShortcut}"
|
||||
TextAlignment="Left" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customeQueryShortcutTips}"
|
||||
TextAlignment="Left"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
<Image
|
||||
Width="478"
|
||||
Margin="0,20,0,0"
|
||||
Source="/Images/illustration_02.png" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Margin="0,10,0,10" Orientation="Horizontal">
|
||||
<Grid Width="478">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customShortcut}" />
|
||||
<TextBox
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Width="180"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
Text="{Binding Key}" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="14"
|
||||
Text="{DynamicResource customShortcutExpansion}" />
|
||||
|
||||
<DockPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
LastChildFill="True">
|
||||
<Button
|
||||
x:Name="btnTestShortcut"
|
||||
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"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Center"
|
||||
Text="{Binding Value}" />
|
||||
</DockPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
<Border
|
||||
Grid.Row="1"
|
||||
Margin="0,10,0,0"
|
||||
Background="{DynamicResource PopupButtonAreaBGColor}"
|
||||
BorderBrush="{DynamicResource PopupButtonAreaBorderColor}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
|
||||
<Button
|
||||
x:Name="btnCancel"
|
||||
MinWidth="140"
|
||||
Margin="10,0,5,0"
|
||||
Click="BtnCancel_OnClick"
|
||||
Content="{DynamicResource cancel}" />
|
||||
<Button
|
||||
x:Name="btnAdd"
|
||||
MinWidth="140"
|
||||
Margin="5,0,10,0"
|
||||
Click="BtnAdd_OnClick"
|
||||
Style="{StaticResource AccentButtonStyle}">
|
||||
<TextBlock x:Name="lblAdd" Text="{DynamicResource done}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
73
Flow.Launcher/CustomShortcutSetting.xaml.cs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
using Flow.Launcher.Core.Resource;
|
||||
using Flow.Launcher.ViewModel;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Flow.Launcher
|
||||
{
|
||||
public partial class CustomShortcutSetting : Window
|
||||
{
|
||||
private SettingWindowViewModel viewModel;
|
||||
public string Key { get; set; } = String.Empty;
|
||||
public string Value { get; set; } = String.Empty;
|
||||
private string originalKey { get; init; } = null;
|
||||
private string originalValue { get; init; } = null;
|
||||
private bool update { get; init; } = false;
|
||||
|
||||
public CustomShortcutSetting(SettingWindowViewModel vm)
|
||||
{
|
||||
viewModel = vm;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public CustomShortcutSetting(string key, string value, SettingWindowViewModel vm)
|
||||
{
|
||||
viewModel = vm;
|
||||
Key = key;
|
||||
Value = value;
|
||||
originalKey = key;
|
||||
originalValue = value;
|
||||
update = true;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void BtnCancel_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void BtnAdd_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (String.IsNullOrEmpty(Key) || String.IsNullOrEmpty(Value))
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("emptyShortcut"));
|
||||
return;
|
||||
}
|
||||
// Check if key is modified or adding a new one
|
||||
if (((update && originalKey != Key) || !update)
|
||||
&& viewModel.ShortcutExists(Key))
|
||||
{
|
||||
MessageBox.Show(InternationalizationManager.Instance.GetTranslation("duplicateShortcut"));
|
||||
return;
|
||||
}
|
||||
DialogResult = !update || originalKey != Key || originalValue != Value;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void cmdEsc_OnPress(object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
private void BtnTestShortcut_OnClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
App.API.ChangeQuery(tbExpand.Text);
|
||||
Application.Current.MainWindow.Show();
|
||||
Application.Current.MainWindow.Opacity = 1;
|
||||
Application.Current.MainWindow.Focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net6.0-windows10.0.19041.0</TargetFramework>
|
||||
<TargetFramework>net7.0-windows10.0.19041.0</TargetFramework>
|
||||
<UseWPF>true</UseWPF>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<StartupObject>Flow.Launcher.App</StartupObject>
|
||||
|
|
@ -83,20 +83,22 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.0.0" />
|
||||
<PackageReference Include="Fody" Version="6.5.4">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="InputSimulator" Version="1.0.4" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.2" />
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.4" />
|
||||
<PackageReference Include="Microsoft.Toolkit.Uwp.Notifications" Version="7.1.3" />
|
||||
<PackageReference Include="ModernWpfUI" Version="0.9.6" />
|
||||
<PackageReference Include="NHotkey.Wpf" Version="2.1.0" />
|
||||
<PackageReference Include="NuGet.CommandLine" Version="5.7.2">
|
||||
<PackageReference Include="NuGet.CommandLine" Version="6.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
|
||||
<PackageReference Include="SharpVectors" Version="1.7.6" />
|
||||
<PackageReference Include="SharpVectors" Version="1.8.1" />
|
||||
<PackageReference Include="VirtualizingWrapPanel" Version="1.5.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
@ -114,4 +116,14 @@
|
|||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
||||
<Exec Command="taskkill /f /fi "IMAGENAME eq Flow.Launcher.exe"" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
<Target Name="RemoveDuplicateAnalyzers" BeforeTargets="CoreCompile">
|
||||
<!-- Work around https://github.com/dotnet/wpf/issues/6792 -->
|
||||
|
||||
<ItemGroup>
|
||||
<FilteredAnalyzer Include="@(Analyzer->Distinct())" />
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
<Analyzer Include="@(FilteredAnalyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ namespace Flow.Launcher.Helper
|
|||
|
||||
public static string DependenciesInfo()
|
||||
{
|
||||
var info = $"\nPython Path: {Constant.PythonPath}";
|
||||
var info = $"\nPython Path: {Constant.PythonPath}\nNode Path: {Constant.NodePath}";
|
||||
return info;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.Hotkey;
|
||||
using Flow.Launcher.Infrastructure.UserSettings;
|
||||
using System;
|
||||
using NHotkey;
|
||||
|
|
@ -17,7 +17,7 @@ namespace Flow.Launcher.Helper
|
|||
internal static void Initialize(MainViewModel mainVM)
|
||||
{
|
||||
mainViewModel = mainVM;
|
||||
settings = mainViewModel._settings;
|
||||
settings = mainViewModel.Settings;
|
||||
|
||||
SetHotkey(settings.Hotkey, OnToggleHotkey);
|
||||
LoadCustomPluginHotkey();
|
||||
|
|
@ -25,7 +25,7 @@ namespace Flow.Launcher.Helper
|
|||
|
||||
internal static void OnToggleHotkey(object sender, HotkeyEventArgs args)
|
||||
{
|
||||
if (!mainViewModel.ShouldIgnoreHotkeys() && !mainViewModel.GameModeStatus)
|
||||
if (!mainViewModel.ShouldIgnoreHotkeys())
|
||||
mainViewModel.ToggleFlowLauncher();
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ namespace Flow.Launcher.Helper
|
|||
{
|
||||
SetHotkey(hotkey.Hotkey, (s, e) =>
|
||||
{
|
||||
if (mainViewModel.ShouldIgnoreHotkeys() || mainViewModel.GameModeStatus)
|
||||
if (mainViewModel.ShouldIgnoreHotkeys())
|
||||
return;
|
||||
|
||||
mainViewModel.Show();
|
||||
|
|
|
|||
|
|
@ -35,25 +35,25 @@ namespace Flow.Launcher.Helper
|
|||
}
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
internal static extern int GetWindowLong(IntPtr hWnd, int nIndex);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
|
||||
internal static extern IntPtr GetForegroundWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetForegroundWindow();
|
||||
internal static extern IntPtr GetDesktopWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetDesktopWindow();
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern IntPtr GetShellWindow();
|
||||
internal static extern IntPtr GetShellWindow();
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
|
||||
internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
|
||||
private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
|
||||
internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
|
||||
|
||||
[DllImport("user32.DLL")]
|
||||
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
|
||||
|
|
|
|||
|
|
@ -38,9 +38,8 @@
|
|||
FontSize="13"
|
||||
FontWeight="SemiBold"
|
||||
Foreground="{DynamicResource Color05B}"
|
||||
Visibility="Visible">
|
||||
Press key
|
||||
</TextBlock>
|
||||
Text="{DynamicResource flowlauncherPressHotkey}"
|
||||
Visibility="Visible" />
|
||||
</Border>
|
||||
</Popup>
|
||||
|
||||
|
|
@ -49,8 +48,9 @@
|
|||
Margin="0,0,18,0"
|
||||
VerticalContentAlignment="Center"
|
||||
input:InputMethod.IsInputMethodEnabled="False"
|
||||
GotFocus="tbHotkey_GotFocus"
|
||||
LostFocus="tbHotkey_LostFocus"
|
||||
PreviewKeyDown="TbHotkey_OnPreviewKeyDown"
|
||||
TabIndex="100"
|
||||
LostFocus="tbHotkey_LostFocus"/>
|
||||
TabIndex="100" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
|
@ -14,22 +14,21 @@ namespace Flow.Launcher
|
|||
{
|
||||
public partial class HotkeyControl : UserControl
|
||||
{
|
||||
private Brush tbMsgForegroundColorOriginal;
|
||||
|
||||
private string tbMsgTextOriginal;
|
||||
|
||||
public HotkeyModel CurrentHotkey { get; private set; }
|
||||
public bool CurrentHotkeyAvailable { get; private set; }
|
||||
|
||||
public event EventHandler HotkeyChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Designed for Preview Hotkey and KeyGesture.
|
||||
/// </summary>
|
||||
public bool ValidateKeyGesture { get; set; } = false;
|
||||
|
||||
protected virtual void OnHotkeyChanged() => HotkeyChanged?.Invoke(this, EventArgs.Empty);
|
||||
|
||||
public HotkeyControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
tbMsgTextOriginal = tbMsg.Text;
|
||||
tbMsgForegroundColorOriginal = tbMsg.Foreground;
|
||||
}
|
||||
|
||||
private CancellationTokenSource hotkeyUpdateSource;
|
||||
|
|
@ -54,9 +53,7 @@ namespace Flow.Launcher
|
|||
specialKeyState.CtrlPressed,
|
||||
key);
|
||||
|
||||
var hotkeyString = hotkeyModel.ToString();
|
||||
|
||||
if (hotkeyString == tbHotkey.Text)
|
||||
if (hotkeyModel.Equals(CurrentHotkey))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -71,49 +68,74 @@ namespace Flow.Launcher
|
|||
|
||||
public async Task SetHotkeyAsync(HotkeyModel keyModel, bool triggerValidate = true)
|
||||
{
|
||||
CurrentHotkey = keyModel;
|
||||
|
||||
tbHotkey.Text = CurrentHotkey.ToString();
|
||||
tbHotkey.Text = keyModel.ToString();
|
||||
tbHotkey.Select(tbHotkey.Text.Length, 0);
|
||||
|
||||
if (triggerValidate)
|
||||
{
|
||||
CurrentHotkeyAvailable = CheckHotkeyAvailability();
|
||||
if (!CurrentHotkeyAvailable)
|
||||
{
|
||||
tbMsg.Foreground = new SolidColorBrush(Colors.Red);
|
||||
tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable");
|
||||
}
|
||||
else
|
||||
{
|
||||
tbMsg.Foreground = new SolidColorBrush(Colors.Green);
|
||||
tbMsg.Text = InternationalizationManager.Instance.GetTranslation("success");
|
||||
}
|
||||
tbMsg.Visibility = Visibility.Visible;
|
||||
bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture);
|
||||
CurrentHotkeyAvailable = hotkeyAvailable;
|
||||
SetMessage(hotkeyAvailable);
|
||||
OnHotkeyChanged();
|
||||
|
||||
var token = hotkeyUpdateSource.Token;
|
||||
await Task.Delay(500, token);
|
||||
if (token.IsCancellationRequested)
|
||||
return;
|
||||
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), null);
|
||||
Keyboard.ClearFocus();
|
||||
|
||||
if (CurrentHotkeyAvailable)
|
||||
{
|
||||
CurrentHotkey = keyModel;
|
||||
// To trigger LostFocus
|
||||
FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), null);
|
||||
Keyboard.ClearFocus();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentHotkey = keyModel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Task SetHotkeyAsync(string keyStr, bool triggerValidate = true)
|
||||
{
|
||||
return SetHotkeyAsync(new HotkeyModel(keyStr), triggerValidate);
|
||||
}
|
||||
|
||||
private bool CheckHotkeyAvailability() => HotKeyMapper.CheckAvailability(CurrentHotkey);
|
||||
private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey);
|
||||
|
||||
public new bool IsFocused => tbHotkey.IsFocused;
|
||||
|
||||
private void tbHotkey_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
tbMsg.Text = tbMsgTextOriginal;
|
||||
tbMsg.Foreground = tbMsgForegroundColorOriginal;
|
||||
tbHotkey.Text = CurrentHotkey?.ToString() ?? "";
|
||||
tbHotkey.Select(tbHotkey.Text.Length, 0);
|
||||
}
|
||||
|
||||
private void tbHotkey_GotFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
ResetMessage();
|
||||
}
|
||||
|
||||
private void ResetMessage()
|
||||
{
|
||||
tbMsg.Text = InternationalizationManager.Instance.GetTranslation("flowlauncherPressHotkey");
|
||||
tbMsg.SetResourceReference(TextBox.ForegroundProperty, "Color05B");
|
||||
}
|
||||
|
||||
private void SetMessage(bool hotkeyAvailable)
|
||||
{
|
||||
if (!hotkeyAvailable)
|
||||
{
|
||||
tbMsg.Foreground = new SolidColorBrush(Colors.Red);
|
||||
tbMsg.Text = InternationalizationManager.Instance.GetTranslation("hotkeyUnavailable");
|
||||
}
|
||||
else
|
||||
{
|
||||
tbMsg.Foreground = new SolidColorBrush(Colors.Green);
|
||||
tbMsg.Text = InternationalizationManager.Instance.GetTranslation("success");
|
||||
}
|
||||
tbMsg.Visibility = Visibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.9 KiB |
BIN
Flow.Launcher/Images/illustration_01.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
Flow.Launcher/Images/illustration_02.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
Flow.Launcher/Images/loading.png
Normal file
|
After Width: | Height: | Size: 274 B |
|
|
@ -1,5 +1,8 @@
|
|||
<?xml version="1.0"?>
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
<?xml version="1.0" ?>
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
<!-- MainWindow -->
|
||||
<system:String x:Key="registerHotkeyFailed">Kunne ikke registrere genvejstast: {0}</system:String>
|
||||
<system:String x:Key="couldnotStartCmd">Kunne ikke starte {0}</system:String>
|
||||
|
|
@ -16,14 +19,18 @@
|
|||
<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="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="PositionReset">Position Reset</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Flow Launcher indstillinger</system:String>
|
||||
<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>
|
||||
|
|
@ -31,7 +38,12 @@
|
|||
<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="rememberLastLocation">Husk seneste position</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</system:String>
|
||||
<system:String x:Key="SearchWindowPositionRememberLastLaunchLocation">Remember Last Position</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenter">Mouse Focused Screen - Center</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenterTop">Mouse Focused Screen - Center Top</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenLeftTop">Mouse Focused Screen - Left Top</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenRightTop">Mouse Focused Screen - Right Top</system:String>
|
||||
<system:String x:Key="language">Sprog</system:String>
|
||||
<system:String x:Key="lastQueryMode">Last Query Style</system:String>
|
||||
<system:String x:Key="lastQueryModeToolTip">Show/Hide previous results when Flow Launcher is reactivated.</system:String>
|
||||
|
|
@ -39,25 +51,39 @@
|
|||
<system:String x:Key="LastQuerySelected">Select last Query</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Empty last Query</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>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">Disable Flow Launcher activation when a full screen application is active (Recommended for games).</system:String>
|
||||
<system:String x:Key="defaultFileManager">Default File Manager</system:String>
|
||||
<system:String x:Key="defaultFileManagerToolTip">Select the file manager to use when opening the folder.</system:String>
|
||||
<system:String x:Key="defaultBrowser">Default Web Browser</system:String>
|
||||
<system:String x:Key="defaultBrowserToolTip">Setting for New Tab, New Window, Private Mode.</system:String>
|
||||
<system:String x:Key="pythonDirectory">Python bibliotek</system:String>
|
||||
<system:String x:Key="pythonFilePath">Python Path</system:String>
|
||||
<system:String x:Key="nodeFilePath">Node.js Path</system:String>
|
||||
<system:String x:Key="selectNodeExecutable">Please select the Node.js executable</system:String>
|
||||
<system:String x:Key="selectPythonExecutable">Please select pythonw.exe</system:String>
|
||||
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
|
||||
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
|
||||
<system:String x:Key="autoUpdates">Autoopdatering</system:String>
|
||||
<system:String x:Key="selectPythonDirectory">Vælg</system:String>
|
||||
<system:String x:Key="select">Vælg</system:String>
|
||||
<system:String x:Key="hideOnStartup">Skjul Flow Launcher ved opstart</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Hide tray icon</system:String>
|
||||
<system:String x:Key="hideNotifyIconToolTip">When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window.</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Query Search Precision</system:String>
|
||||
<system:String x:Key="querySearchPrecisionToolTip">Changes minimum match score required for results.</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Should Use 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">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="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>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
<system:String x:Key="searchpluginToolTip">Ctrl+F to search plugins</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Title">No results found</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">Please try a different search.</system:String>
|
||||
<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="disable">Deaktiver</system:String>
|
||||
|
|
@ -69,24 +95,47 @@
|
|||
<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="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_version">Version</system:String>
|
||||
<system:String x:Key="plugin_query_web">Website</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
||||
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plugin Store</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">Plugin</system:String>
|
||||
<system:String x:Key="pluginStore_Installed">Installed</system:String>
|
||||
<system:String x:Key="refresh">Refresh</system:String>
|
||||
<system:String x:Key="install">Install</system:String>
|
||||
<system:String x:Key="installbtn">Install</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>
|
||||
<system:String x:Key="LabelNew">New Version</system:String>
|
||||
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
|
||||
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>
|
||||
|
||||
|
||||
|
||||
<!-- Setting Theme -->
|
||||
<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="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>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">Search the web with different search engine support</system:String>
|
||||
<system:String x:Key="SampleTitleProgram">Program</system:String>
|
||||
<system:String x:Key="SampleSubTitleProgram">Launch programs as admin or a different user</system:String>
|
||||
<system:String x:Key="SampleTitleProcessKiller">ProcessKiller</system:String>
|
||||
<system:String x:Key="SampleSubTitleProcessKiller">Terminate unwanted processes</system:String>
|
||||
<system:String x:Key="queryBoxFont">Søgefelt skrifttype</system:String>
|
||||
<system:String x:Key="resultItemFont">Resultat skrifttype</system:String>
|
||||
<system:String x:Key="windowMode">Vindue mode</system:String>
|
||||
|
|
@ -103,27 +152,42 @@
|
|||
<system:String x:Key="SoundEffectTip">Play a small sound when the search window opens</system:String>
|
||||
<system:String x:Key="Animation">Animation</system:String>
|
||||
<system:String x:Key="AnimationTip">Use Animation in UI</system:String>
|
||||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Genvejstast</system:String>
|
||||
<system:String x:Key="hotkeys">Genvejstast</system:String>
|
||||
<system:String x:Key="flowlauncherHotkey">Flow Launcher genvejstast</system:String>
|
||||
<system:String x:Key="flowlauncherHotkeyToolTip">Enter shortcut to show/hide Flow Launcher.</system:String>
|
||||
<system:String x:Key="previewHotkey">Preview Hotkey</system:String>
|
||||
<system:String x:Key="previewHotkeyToolTip">Enter shortcut to show/hide preview in search window.</system:String>
|
||||
<system:String x:Key="openResultModifiers">Åbn resultatmodifikatorer</system:String>
|
||||
<system:String x:Key="openResultModifiersToolTip">Select a modifier key to open selected result via keyboard.</system:String>
|
||||
<system:String x:Key="showOpenResultHotkey">Vis hotkey</system:String>
|
||||
<system:String x:Key="showOpenResultHotkeyToolTip">Show result selection hotkey with results.</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Tilpasset søgegenvejstast</system:String>
|
||||
<system:String x:Key="customQueryShortcut">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="builtinShortcuts">Built-in Shortcut</system:String>
|
||||
<system:String x:Key="customQuery">Query</system:String>
|
||||
<system:String x:Key="customShortcut">Shortcut</system:String>
|
||||
<system:String x:Key="customShortcutExpansion">Expansion</system:String>
|
||||
<system:String x:Key="builtinShortcutDescription">Description</system:String>
|
||||
<system:String x:Key="delete">Slet</system:String>
|
||||
<system:String x:Key="edit">Rediger</system:String>
|
||||
<system:String x:Key="add">Tilføj</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Vælg venligst</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">Er du sikker på du vil slette {0} plugin genvejstast?</system:String>
|
||||
<system:String x:Key="deleteCustomShortcutWarning">Are you sure you want to delete shortcut: {0} with expansion {1}?</system:String>
|
||||
<system:String x:Key="shortcut_clipboard_description">Get text from clipboard.</system:String>
|
||||
<system:String x:Key="shortcut_active_explorer_path">Get path from active explorer.</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Query window shadow effect</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited.</system:String>
|
||||
<system:String x:Key="windowWidthSize">Window Width Size</system:String>
|
||||
<system:String x:Key="windowWidthSizeToolTip">You can also quickly adjust this by using Ctrl+[ and Ctrl+].</system:String>
|
||||
<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>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -144,22 +208,26 @@
|
|||
<!-- Setting About -->
|
||||
<system:String x:Key="about">Om</system:String>
|
||||
<system:String x:Key="website">Website</system:String>
|
||||
<system:String x:Key="github">Github</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>
|
||||
<system:String x:Key="icons">Icons</system:String>
|
||||
<system:String x:Key="about_activate_times">Du har aktiveret Flow Launcher {0} gange</system:String>
|
||||
<system:String x:Key="checkUpdates">Tjek for opdateringer</system:String>
|
||||
<system:String x:Key="BecomeASponsor">Become A Sponsor</system:String>
|
||||
<system:String x:Key="newVersionTips">Ny version {0} er tilgængelig, genstart venligst Flow Launcher</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Check updates failed, please check your connection and proxy settings to api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com,
|
||||
or go to https://github.com/Flow-Launcher/Flow.Launcher/releases to download updates manually.
|
||||
</system:String>
|
||||
<system:String x:Key="releaseNotes">Release Notes:</system:String>
|
||||
<system:String x:Key="releaseNotes">Release Notes</system:String>
|
||||
<system:String x:Key="documentation">Usage Tips</system:String>
|
||||
<system:String x:Key="devtool">DevTools</system:String>
|
||||
<system:String x:Key="settingfolder">Setting Folder</system:String>
|
||||
<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="welcomewindow">Wizard</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
|
|
@ -201,12 +269,18 @@
|
|||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Tilpasset søgegenvejstast</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press the custom hotkey to automatically insert the specified query.</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press a custom hotkey to open Flow Launcher and input the specified query automatically.</system:String>
|
||||
<system:String x:Key="preview">Vis</system:String>
|
||||
<system:String x:Key="hotkeyIsNotUnavailable">Genvejstast er utilgængelig, vælg venligst en ny genvejstast</system:String>
|
||||
<system:String x:Key="invalidPluginHotkey">Ugyldig plugin genvejstast</system:String>
|
||||
<system:String x:Key="update">Opdater</system:String>
|
||||
|
||||
<!-- Custom Query Shortcut Dialog -->
|
||||
<system:String x:Key="customeQueryShortcutTitle">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="customeQueryShortcutTips">Enter a shortcut that automatically expands to the specified query.</system:String>
|
||||
<system:String x:Key="duplicateShortcut">Shortcut already exists, please enter a new Shortcut or edit the existing one.</system:String>
|
||||
<system:String x:Key="emptyShortcut">Shortcut and/or its expansion is empty.</system:String>
|
||||
|
||||
<!-- Hotkey Control -->
|
||||
<system:String x:Key="hotkeyUnavailable">Genvejstast utilgængelig</system:String>
|
||||
|
||||
|
|
@ -229,7 +303,7 @@
|
|||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Please wait...</system:String>
|
||||
|
||||
<!-- update -->
|
||||
<!-- Update -->
|
||||
<system:String x:Key="update_flowlauncher_update_check">Checking for new update</system:String>
|
||||
<system:String x:Key="update_flowlauncher_already_on_latest">You already have the latest Flow Launcher version</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_found">Update found</system:String>
|
||||
|
|
@ -269,7 +343,7 @@
|
|||
<system:String x:Key="HotkeyUpDownDesc">Back / Context Menu</system:String>
|
||||
<system:String x:Key="HotkeyLeftRightDesc">Item Navigation</system:String>
|
||||
<system:String x:Key="HotkeyShiftEnterDesc">Open Context Menu</system:String>
|
||||
<system:String x:Key="HotkeyCtrlEnterDesc">Open Contaning Folder</system:String>
|
||||
<system:String x:Key="HotkeyCtrlEnterDesc">Open Containing Folder</system:String>
|
||||
<system:String x:Key="HotkeyCtrlShiftEnterDesc">Run as Admin</system:String>
|
||||
<system:String x:Key="HotkeyCtrlHDesc">Query History</system:String>
|
||||
<system:String x:Key="HotkeyESCDesc">Back to Result in Context Menu</system:String>
|
||||
|
|
@ -282,7 +356,7 @@
|
|||
<system:String x:Key="RecommendWeatherDesc">Weather in Google Result</system:String>
|
||||
<system:String x:Key="RecommendShell">> ping 8.8.8.8</system:String>
|
||||
<system:String x:Key="RecommendShellDesc">Shell Command</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">s Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetoothDesc">Bluetooth in Windows Settings</system:String>
|
||||
<system:String x:Key="RecommendAcronyms">sn</system:String>
|
||||
<system:String x:Key="RecommendAcronymsDesc">Sticky Notes</system:String>
|
||||
|
|
|
|||
|
|
@ -16,14 +16,18 @@
|
|||
<system:String x:Key="copy">Kopieren</system:String>
|
||||
<system:String x:Key="cut">Ausschneiden</system:String>
|
||||
<system:String x:Key="paste">Einfügen</system:String>
|
||||
<system:String x:Key="undo">Rückgängig</system:String>
|
||||
<system:String x:Key="selectAll">Alle auswählen</system:String>
|
||||
<system:String x:Key="fileTitle">Datei</system:String>
|
||||
<system:String x:Key="folderTitle">Ordner</system:String>
|
||||
<system:String x:Key="textTitle">Text</system:String>
|
||||
<system:String x:Key="GameMode">Spielmodus</system:String>
|
||||
<system:String x:Key="GameModeToolTip">Hotkeys deaktivieren.</system:String>
|
||||
<system:String x:Key="PositionReset">Position Reset</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Flow Launcher Einstellungen</system:String>
|
||||
<system:String x:Key="flowlauncher_settings">Einstellungen</system:String>
|
||||
<system:String x:Key="general">Allgemein</system:String>
|
||||
<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 mit Wechseldatenträgern oder Clouddiensten).</system:String>
|
||||
|
|
@ -31,7 +35,12 @@
|
|||
<system:String x:Key="setAutoStartFailed">Error setting launch on startup</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Verstecke Flow Launcher wenn der Fokus verloren geht</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">Zeige keine Nachricht wenn eine neue Version vorhanden ist</system:String>
|
||||
<system:String x:Key="rememberLastLocation">Merke letzte Ausführungsposition</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</system:String>
|
||||
<system:String x:Key="SearchWindowPositionRememberLastLaunchLocation">Remember Last Position</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenter">Mouse Focused Screen - Center</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenterTop">Mouse Focused Screen - Center Top</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenLeftTop">Mouse Focused Screen - Left Top</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenRightTop">Mouse Focused Screen - Right Top</system:String>
|
||||
<system:String x:Key="language">Sprache</system:String>
|
||||
<system:String x:Key="lastQueryMode">Abfragestil auswählen</system:String>
|
||||
<system:String x:Key="lastQueryModeToolTip">Vorherige Ergebnisse ein-/ausblenden, wenn Flow Launcher wieder aktiviert wird.</system:String>
|
||||
|
|
@ -39,25 +48,39 @@
|
|||
<system:String x:Key="LastQuerySelected">Letzte Abfrage auswählen</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Letzte Abfrage leeren</system:String>
|
||||
<system:String x:Key="maxShowResults">Maximale Anzahl Ergebnissen</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">Ignoriere Tastenkombination wenn Fenster im Vollbildmodus ist</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">Deaktiviere Flow Launcher, wenn eine Vollbildanwendung aktiv ist (Empfohlen für Spiele).</system:String>
|
||||
<system:String x:Key="defaultFileManager">Standard-Dateimanager</system:String>
|
||||
<system:String x:Key="defaultFileManagerToolTip">Wählen Sie den Dateimanager, der beim Öffnen des Ordners verwendet werden soll.</system:String>
|
||||
<system:String x:Key="defaultBrowser">Standardbrowser</system:String>
|
||||
<system:String x:Key="defaultBrowserToolTip">Einstellung für neuen Tab, neues Fenster und dem Privatmodus.</system:String>
|
||||
<system:String x:Key="pythonDirectory">Python-Verzeichnis</system:String>
|
||||
<system:String x:Key="pythonFilePath">Python-Pfad</system:String>
|
||||
<system:String x:Key="nodeFilePath">Node.js-Pfad</system:String>
|
||||
<system:String x:Key="selectNodeExecutable">Bitte wählen Sie das Programm Node.js aus</system:String>
|
||||
<system:String x:Key="selectPythonExecutable">Bitte wählen Sie pythonw.exe aus</system:String>
|
||||
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
|
||||
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
|
||||
<system:String x:Key="autoUpdates">Automatische Aktualisierung</system:String>
|
||||
<system:String x:Key="selectPythonDirectory">Auswählen</system:String>
|
||||
<system:String x:Key="select">Auswählen</system:String>
|
||||
<system:String x:Key="hideOnStartup">Verstecke Flow Launcher bei Systemstart</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Statusleistensymbol ausblenden</system:String>
|
||||
<system:String x:Key="hideNotifyIconToolTip">When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window.</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Suchgenauigkeit abfragen</system:String>
|
||||
<system:String x:Key="querySearchPrecisionToolTip">Erforderliche Suchergebnisse.</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Pinyin aktivieren</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">Ermöglicht die Verwendung von Pinyin für die Suche. Pinyin ist das Standardsystem der romanisierten Schreibweise für die Übersetzung von chinesischen Texten</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">Ermöglicht die Verwendung von Pinyin für die Suche. Pinyin ist das Standardsystem der romanisierten Schreibweise für die Übersetzung von chinesischen Texten.</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">Der Schatteneffekt ist nicht zulässig, wenn das aktuelle Thema den Weichzeichneffekt aktiviert hat</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
<system:String x:Key="searchpluginToolTip">Ctrl+F to search plugins</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Title">No results found</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">Please try a different search.</system:String>
|
||||
<system:String x:Key="plugin">Erweiterung</system:String>
|
||||
<system:String x:Key="plugins">Erweiterung</system:String>
|
||||
<system:String x:Key="browserMorePlugins">Suche nach weiteren Plugins</system:String>
|
||||
<system:String x:Key="enable">Aktivieren</system:String>
|
||||
<system:String x:Key="disable">Deaktivieren</system:String>
|
||||
|
|
@ -69,24 +92,47 @@
|
|||
<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>
|
||||
<system:String x:Key="priorityToolTip">Change Plugin Results Priority</system:String>
|
||||
<system:String x:Key="pluginDirectory">Pluginordner</system:String>
|
||||
<system:String x:Key="author">von</system:String>
|
||||
<system:String x:Key="plugin_init_time">Initialisierungszeit:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Abfragezeit:</system:String>
|
||||
<system:String x:Key="plugin_query_version">Version</system:String>
|
||||
<system:String x:Key="plugin_query_web">Webseite</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Deinstallieren</system:String>
|
||||
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Erweiterungen laden</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">Plugin</system:String>
|
||||
<system:String x:Key="pluginStore_Installed">Installed</system:String>
|
||||
<system:String x:Key="refresh">Aktualisieren</system:String>
|
||||
<system:String x:Key="install">Installieren</system:String>
|
||||
<system:String x:Key="installbtn">Installieren</system:String>
|
||||
<system:String x:Key="uninstallbtn">Deinstallieren</system:String>
|
||||
<system:String x:Key="updatebtn">Aktualisieren</system:String>
|
||||
<system:String x:Key="LabelInstalledToolTip">Plugin ist bereits installiert</system:String>
|
||||
<system:String x:Key="LabelNew">New Version</system:String>
|
||||
<system:String x:Key="LabelNewToolTip">Dieses Plugin wurde innerhalb der letzten 7 Tage aktualisiert</system:String>
|
||||
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>
|
||||
|
||||
|
||||
|
||||
<!-- Setting Theme -->
|
||||
<system:String x:Key="theme">Design</system:String>
|
||||
<system:String x:Key="appearance">Appearance</system:String>
|
||||
<system:String x:Key="browserMoreThemes">Suche nach weiteren Themes</system:String>
|
||||
<system:String x:Key="howToCreateTheme">Wie man ein Design erstellt</system:String>
|
||||
<system:String x:Key="hiThere">Hallo!</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>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">Search the web with different search engine support</system:String>
|
||||
<system:String x:Key="SampleTitleProgram">Programm</system:String>
|
||||
<system:String x:Key="SampleSubTitleProgram">Launch programs as admin or a different user</system:String>
|
||||
<system:String x:Key="SampleTitleProcessKiller">ProcessKiller</system:String>
|
||||
<system:String x:Key="SampleSubTitleProcessKiller">Terminate unwanted processes</system:String>
|
||||
<system:String x:Key="queryBoxFont">Abfragebox Schriftart</system:String>
|
||||
<system:String x:Key="resultItemFont">Ergebnis Schriftart</system:String>
|
||||
<system:String x:Key="windowMode">Fenstermodus</system:String>
|
||||
|
|
@ -103,32 +149,47 @@
|
|||
<system:String x:Key="SoundEffectTip">Ton abspielen, wenn das Suchfenster geöffnet wird</system:String>
|
||||
<system:String x:Key="Animation">Animation</system:String>
|
||||
<system:String x:Key="AnimationTip">Animationen in der Oberfläche verwenden</system:String>
|
||||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Tastenkombination</system:String>
|
||||
<system:String x:Key="hotkeys">Tastenkombination</system:String>
|
||||
<system:String x:Key="flowlauncherHotkey">Flow Launcher Tastenkombination</system:String>
|
||||
<system:String x:Key="flowlauncherHotkeyToolTip">Verknüpfung eingeben, um Flow Launcher anzuzeigen/auszublenden.</system:String>
|
||||
<system:String x:Key="previewHotkey">Preview Hotkey</system:String>
|
||||
<system:String x:Key="previewHotkeyToolTip">Enter shortcut to show/hide preview in search window.</system:String>
|
||||
<system:String x:Key="openResultModifiers">Öffnen Sie die Ergebnismodifikatoren</system:String>
|
||||
<system:String x:Key="openResultModifiersToolTip">Select a modifier key to open selected result via keyboard.</system:String>
|
||||
<system:String x:Key="showOpenResultHotkey">Hotkey anzeigen</system:String>
|
||||
<system:String x:Key="showOpenResultHotkeyToolTip">Show result selection hotkey with results.</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Benutzerdefinierte Abfrage Tastenkombination</system:String>
|
||||
<system:String x:Key="customQueryShortcut">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="builtinShortcuts">Built-in Shortcut</system:String>
|
||||
<system:String x:Key="customQuery">Query</system:String>
|
||||
<system:String x:Key="customShortcut">Shortcut</system:String>
|
||||
<system:String x:Key="customShortcutExpansion">Expansion</system:String>
|
||||
<system:String x:Key="builtinShortcutDescription">Beschreibung</system:String>
|
||||
<system:String x:Key="delete">Löschen</system:String>
|
||||
<system:String x:Key="edit">Bearbeiten</system:String>
|
||||
<system:String x:Key="add">Hinzufügen</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Bitte einen Eintrag auswählen</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">Wollen Sie die {0} Plugin Tastenkombination wirklich löschen?</system:String>
|
||||
<system:String x:Key="deleteCustomShortcutWarning">Are you sure you want to delete shortcut: {0} with expansion {1}?</system:String>
|
||||
<system:String x:Key="shortcut_clipboard_description">Get text from clipboard.</system:String>
|
||||
<system:String x:Key="shortcut_active_explorer_path">Get path from active explorer.</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Query window shadow effect</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited.</system:String>
|
||||
<system:String x:Key="windowWidthSize">Window Width Size</system:String>
|
||||
<system:String x:Key="windowWidthSizeToolTip">You can also quickly adjust this by using Ctrl+[ and Ctrl+].</system:String>
|
||||
<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>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
<system:String x:Key="proxy">HTTP-Proxy</system:String>
|
||||
<system:String x:Key="enableProxy">Aktiviere HTTP Proxy</system:String>
|
||||
<system:String x:Key="server">HTTP Server</system:String>
|
||||
<system:String x:Key="server">HTTP-Server</system:String>
|
||||
<system:String x:Key="port">Port</system:String>
|
||||
<system:String x:Key="userName">Benutzername</system:String>
|
||||
<system:String x:Key="password">Passwort</system:String>
|
||||
|
|
@ -143,47 +204,51 @@
|
|||
|
||||
<!-- Setting About -->
|
||||
<system:String x:Key="about">Über</system:String>
|
||||
<system:String x:Key="website">Website</system:String>
|
||||
<system:String x:Key="github">Github</system:String>
|
||||
<system:String x:Key="docs">Docs</system:String>
|
||||
<system:String x:Key="website">Webseite</system:String>
|
||||
<system:String x:Key="github">GitHub</system:String>
|
||||
<system:String x:Key="docs">Dokumentation</system:String>
|
||||
<system:String x:Key="version">Version</system:String>
|
||||
<system:String x:Key="icons">Icons</system:String>
|
||||
<system:String x:Key="about_activate_times">Sie haben Flow Launcher {0} mal aktiviert</system:String>
|
||||
<system:String x:Key="checkUpdates">Nach Aktuallisierungen Suchen</system:String>
|
||||
<system:String x:Key="BecomeASponsor">Become A Sponsor</system:String>
|
||||
<system:String x:Key="newVersionTips">Eine neue Version ({0}) ist vorhanden. Bitte starten Sie Flow Launcher neu.</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Check updates failed, please check your connection and proxy settings to api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
Download updates failed, please check your connection and proxy settings to github-cloud.s3.amazonaws.com,
|
||||
or go to https://github.com/Flow-Launcher/Flow.Launcher/releases to download updates manually.
|
||||
</system:String>
|
||||
<system:String x:Key="releaseNotes">Versionshinweise:</system:String>
|
||||
<system:String x:Key="releaseNotes">Versionshinweise</system:String>
|
||||
<system:String x:Key="documentation">Usage Tips</system:String>
|
||||
<system:String x:Key="devtool">DevTools</system:String>
|
||||
<system:String x:Key="settingfolder">Setting Folder</system:String>
|
||||
<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="welcomewindow">Wizard</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
<system:String x:Key="fileManagerWindow">Select File Manager</system:String>
|
||||
<system:String x:Key="fileManager_tips">Please specify the file location of the file manager you using and add arguments if necessary. The default arguments are "%d", and a path is entered at that location. For example, If a command is required such as "totalcmd.exe /A c:\windows", argument is /A "%d".</system:String>
|
||||
<system:String x:Key="fileManager_tips2">"%f" is an argument that represent the file path. It is used to emphasize the file/folder name when opening a specific file location in 3rd party file manager. This argument is only available in the "Arg for File" item. If the file manager does not have that function, you can use "%d".</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_name">Datei-Manager</system:String>
|
||||
<system:String x:Key="fileManager_profile_name">Profilname</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>
|
||||
|
||||
<!-- DefaultBrowser Setting Dialog -->
|
||||
<system:String x:Key="defaultBrowserTitle">Default Web Browser</system:String>
|
||||
<system:String x:Key="defaultBrowserTitle">Standard-Webbrowser</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">Browserpfad</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">Privater Modus</system:String>
|
||||
|
||||
<!-- Priority Setting Dialog -->
|
||||
<system:String x:Key="changePriorityWindow">Change Priority</system:String>
|
||||
<system:String x:Key="changePriorityWindow">Priorität ändern</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>
|
||||
|
||||
|
|
@ -201,12 +266,18 @@
|
|||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Benutzerdefinierte Abfrage Tastenkombination</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press the custom hotkey to automatically insert the specified query.</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press a custom hotkey to open Flow Launcher and input the specified query automatically.</system:String>
|
||||
<system:String x:Key="preview">Vorschau</system:String>
|
||||
<system:String x:Key="hotkeyIsNotUnavailable">Tastenkombination ist nicht verfügbar, bitte wähle eine andere Tastenkombination</system:String>
|
||||
<system:String x:Key="invalidPluginHotkey">Ungültige Plugin Tastenkombination</system:String>
|
||||
<system:String x:Key="update">Aktualisieren</system:String>
|
||||
|
||||
<!-- Custom Query Shortcut Dialog -->
|
||||
<system:String x:Key="customeQueryShortcutTitle">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="customeQueryShortcutTips">Enter a shortcut that automatically expands to the specified query.</system:String>
|
||||
<system:String x:Key="duplicateShortcut">Shortcut already exists, please enter a new Shortcut or edit the existing one.</system:String>
|
||||
<system:String x:Key="emptyShortcut">Shortcut and/or its expansion is empty.</system:String>
|
||||
|
||||
<!-- Hotkey Control -->
|
||||
<system:String x:Key="hotkeyUnavailable">Tastenkombination nicht verfügbar</system:String>
|
||||
|
||||
|
|
@ -229,7 +300,7 @@
|
|||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Bitte warten...</system:String>
|
||||
|
||||
<!-- update -->
|
||||
<!-- Update -->
|
||||
<system:String x:Key="update_flowlauncher_update_check">Nach Updates suchen!</system:String>
|
||||
<system:String x:Key="update_flowlauncher_already_on_latest">Sie haben bereits die neuste Version</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_found">Update gefunden</system:String>
|
||||
|
|
@ -269,7 +340,7 @@
|
|||
<system:String x:Key="HotkeyUpDownDesc">Back / Context Menu</system:String>
|
||||
<system:String x:Key="HotkeyLeftRightDesc">Item Navigation</system:String>
|
||||
<system:String x:Key="HotkeyShiftEnterDesc">Open Context Menu</system:String>
|
||||
<system:String x:Key="HotkeyCtrlEnterDesc">Open Contaning Folder</system:String>
|
||||
<system:String x:Key="HotkeyCtrlEnterDesc">Open Containing Folder</system:String>
|
||||
<system:String x:Key="HotkeyCtrlShiftEnterDesc">Run as Admin</system:String>
|
||||
<system:String x:Key="HotkeyCtrlHDesc">Query History</system:String>
|
||||
<system:String x:Key="HotkeyESCDesc">Back to Result in Context Menu</system:String>
|
||||
|
|
@ -282,7 +353,7 @@
|
|||
<system:String x:Key="RecommendWeatherDesc">Weather in Google Result</system:String>
|
||||
<system:String x:Key="RecommendShell">> ping 8.8.8.8</system:String>
|
||||
<system:String x:Key="RecommendShellDesc">Shell Command</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">s Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetoothDesc">Bluetooth in Windows Settings</system:String>
|
||||
<system:String x:Key="RecommendAcronyms">sn</system:String>
|
||||
<system:String x:Key="RecommendAcronymsDesc">Sticky Notes</system:String>
|
||||
|
|
|
|||
|
|
@ -18,14 +18,18 @@
|
|||
<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="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="PositionReset">Position Reset</system:String>
|
||||
<system:String x:Key="PositionResetToolTip">Reset search window position</system:String>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Flow Launcher Settings</system:String>
|
||||
<system:String x:Key="flowlauncher_settings">Settings</system:String>
|
||||
<system:String x:Key="general">General</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>
|
||||
|
|
@ -33,7 +37,18 @@
|
|||
<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="rememberLastLocation">Remember last launch location</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</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>
|
||||
<system:String x:Key="SearchWindowScreenPrimary">Primary Monitor</system:String>
|
||||
<system:String x:Key="SearchWindowScreenCustom">Custom Monitor</system:String>
|
||||
<system:String x:Key="SearchWindowAlign">Search Window Position on Monitor</system:String>
|
||||
<system:String x:Key="SearchWindowAlignCenter">Center</system:String>
|
||||
<system:String x:Key="SearchWindowAlignCenterTop">Center Top</system:String>
|
||||
<system:String x:Key="SearchWindowAlignLeftTop">Left Top</system:String>
|
||||
<system:String x:Key="SearchWindowAlignRightTop">Right Top</system:String>
|
||||
<system:String x:Key="SearchWindowAlignCustom">Custom Position</system:String>
|
||||
<system:String x:Key="language">Language</system:String>
|
||||
<system:String x:Key="lastQueryMode">Last Query Style</system:String>
|
||||
<system:String x:Key="lastQueryModeToolTip">Show/Hide previous results when Flow Launcher is reactivated.</system:String>
|
||||
|
|
@ -41,25 +56,39 @@
|
|||
<system:String x:Key="LastQuerySelected">Select last Query</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Empty last Query</system:String>
|
||||
<system:String x:Key="maxShowResults">Maximum results shown</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">Ignore hotkeys in fullscreen mode</system:String>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">Disable Flow Launcher activation when a full screen application is active (Recommended for games).</system:String>
|
||||
<system:String x:Key="defaultFileManager">Default File Manager</system:String>
|
||||
<system:String x:Key="defaultFileManagerToolTip">Select the file manager to use when opening the folder.</system:String>
|
||||
<system:String x:Key="defaultBrowser">Default Web Browser</system:String>
|
||||
<system:String x:Key="defaultBrowserToolTip">Setting for New Tab, New Window, Private Mode.</system:String>
|
||||
<system:String x:Key="pythonDirectory">Python Directory</system:String>
|
||||
<system:String x:Key="pythonFilePath">Python Path</system:String>
|
||||
<system:String x:Key="nodeFilePath">Node.js Path</system:String>
|
||||
<system:String x:Key="selectNodeExecutable">Please select the Node.js executable</system:String>
|
||||
<system:String x:Key="selectPythonExecutable">Please select pythonw.exe</system:String>
|
||||
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
|
||||
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
|
||||
<system:String x:Key="autoUpdates">Auto Update</system:String>
|
||||
<system:String x:Key="selectPythonDirectory">Select</system:String>
|
||||
<system:String x:Key="select">Select</system:String>
|
||||
<system:String x:Key="hideOnStartup">Hide Flow Launcher on startup</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Hide tray icon</system:String>
|
||||
<system:String x:Key="hideNotifyIconToolTip">When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window.</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Query Search Precision</system:String>
|
||||
<system:String x:Key="querySearchPrecisionToolTip">Changes minimum match score required for results.</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Should Use 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">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="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>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
<system:String x:Key="searchpluginToolTip">Ctrl+F to search plugins</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Title">No results found</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">Please try a different search.</system:String>
|
||||
<system:String x:Key="plugin">Plugin</system:String>
|
||||
<system:String x:Key="plugins">Plugins</system:String>
|
||||
<system:String x:Key="browserMorePlugins">Find more plugins</system:String>
|
||||
<system:String x:Key="enable">On</system:String>
|
||||
<system:String x:Key="disable">Off</system:String>
|
||||
|
|
@ -71,24 +100,47 @@
|
|||
<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="priorityToolTip">Change Plugin Results Priority</system:String>
|
||||
<system:String x:Key="pluginDirectory">Plugin Directory</system:String>
|
||||
<system:String x:Key="author">by</system:String>
|
||||
<system:String x:Key="plugin_init_time">Init time:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Query time:</system:String>
|
||||
<system:String x:Key="plugin_query_version">| Version</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_uninstall">Uninstall</system:String>
|
||||
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Plugin Store</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="install">Install</system:String>
|
||||
<system:String x:Key="installbtn">Install</system:String>
|
||||
<system:String x:Key="uninstallbtn">Uninstall</system:String>
|
||||
<system:String x:Key="updatebtn">Update</system:String>
|
||||
<system:String x:Key="LabelInstalledToolTip">Plugin already installed</system:String>
|
||||
<system:String x:Key="LabelNew">New Version</system:String>
|
||||
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
|
||||
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>
|
||||
|
||||
|
||||
|
||||
<!-- Setting Theme -->
|
||||
<system:String x:Key="theme">Theme</system:String>
|
||||
<system:String x:Key="appearance">Appearance</system:String>
|
||||
<system:String x:Key="browserMoreThemes">Theme Gallery</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="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>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">Search the web with different search engine support</system:String>
|
||||
<system:String x:Key="SampleTitleProgram">Program</system:String>
|
||||
<system:String x:Key="SampleSubTitleProgram">Launch programs as admin or a different user</system:String>
|
||||
<system:String x:Key="SampleTitleProcessKiller">ProcessKiller</system:String>
|
||||
<system:String x:Key="SampleSubTitleProcessKiller">Terminate unwanted processes</system:String>
|
||||
<system:String x:Key="queryBoxFont">Query Box Font</system:String>
|
||||
<system:String x:Key="resultItemFont">Result Item Font</system:String>
|
||||
<system:String x:Key="windowMode">Window Mode</system:String>
|
||||
|
|
@ -105,27 +157,42 @@
|
|||
<system:String x:Key="SoundEffectTip">Play a small sound when the search window opens</system:String>
|
||||
<system:String x:Key="Animation">Animation</system:String>
|
||||
<system:String x:Key="AnimationTip">Use Animation in UI</system:String>
|
||||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Hotkey</system:String>
|
||||
<system:String x:Key="hotkeys">Hotkeys</system:String>
|
||||
<system:String x:Key="flowlauncherHotkey">Flow Launcher Hotkey</system:String>
|
||||
<system:String x:Key="flowlauncherHotkeyToolTip">Enter shortcut to show/hide Flow Launcher.</system:String>
|
||||
<system:String x:Key="previewHotkey">Preview Hotkey</system:String>
|
||||
<system:String x:Key="previewHotkeyToolTip">Enter shortcut to show/hide preview in search window.</system:String>
|
||||
<system:String x:Key="openResultModifiers">Open Result Modifier Key</system:String>
|
||||
<system:String x:Key="openResultModifiersToolTip">Select a modifier key to open selected result via keyboard.</system:String>
|
||||
<system:String x:Key="showOpenResultHotkey">Show Hotkey</system:String>
|
||||
<system:String x:Key="showOpenResultHotkeyToolTip">Show result selection hotkey with results.</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Custom Query Hotkey</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Custom Query Hotkeys</system:String>
|
||||
<system:String x:Key="customQueryShortcut">Custom Query Shortcuts</system:String>
|
||||
<system:String x:Key="builtinShortcuts">Built-in Shortcuts</system:String>
|
||||
<system:String x:Key="customQuery">Query</system:String>
|
||||
<system:String x:Key="customShortcut">Shortcut</system:String>
|
||||
<system:String x:Key="customShortcutExpansion">Expansion</system:String>
|
||||
<system:String x:Key="builtinShortcutDescription">Description</system:String>
|
||||
<system:String x:Key="delete">Delete</system:String>
|
||||
<system:String x:Key="edit">Edit</system:String>
|
||||
<system:String x:Key="add">Add</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Please select an item</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">Are you sure you want to delete {0} plugin hotkey?</system:String>
|
||||
<system:String x:Key="deleteCustomShortcutWarning">Are you sure you want to delete shortcut: {0} with expansion {1}?</system:String>
|
||||
<system:String x:Key="shortcut_clipboard_description">Get text from clipboard.</system:String>
|
||||
<system:String x:Key="shortcut_active_explorer_path">Get path from active explorer.</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Query window shadow effect</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">Shadow effect has a substantial usage of GPU. Not recommended if your computer performance is limited.</system:String>
|
||||
<system:String x:Key="windowWidthSize">Window Width Size</system:String>
|
||||
<system:String x:Key="windowWidthSizeToolTip">You can also quickly adjust this by using Ctrl+[ and Ctrl+].</system:String>
|
||||
<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>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">HTTP Proxy</system:String>
|
||||
|
|
@ -146,11 +213,13 @@
|
|||
<!-- Setting About -->
|
||||
<system:String x:Key="about">About</system:String>
|
||||
<system:String x:Key="website">Website</system:String>
|
||||
<system:String x:Key="github">Github</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>
|
||||
<system:String x:Key="icons">Icons</system:String>
|
||||
<system:String x:Key="about_activate_times">You have activated Flow Launcher {0} times</system:String>
|
||||
<system:String x:Key="checkUpdates">Check for Updates</system:String>
|
||||
<system:String x:Key="BecomeASponsor">Become A Sponsor</system:String>
|
||||
<system:String x:Key="newVersionTips">New version {0} is available, would you like to restart Flow Launcher to use the update?</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Check updates failed, please check your connection and proxy settings to api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
|
|
@ -162,6 +231,8 @@
|
|||
<system:String x:Key="devtool">DevTools</system:String>
|
||||
<system:String x:Key="settingfolder">Setting Folder</system:String>
|
||||
<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="welcomewindow">Wizard</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
|
|
@ -203,12 +274,18 @@
|
|||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Custom Query Hotkey</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press the custom hotkey to automatically insert the specified query.</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Press a custom hotkey to open Flow Launcher and input the specified query automatically.</system:String>
|
||||
<system:String x:Key="preview">Preview</system:String>
|
||||
<system:String x:Key="hotkeyIsNotUnavailable">Hotkey is unavailable, please select a new hotkey</system:String>
|
||||
<system:String x:Key="invalidPluginHotkey">Invalid plugin hotkey</system:String>
|
||||
<system:String x:Key="update">Update</system:String>
|
||||
|
||||
<!-- Custom Query Shortcut Dialog -->
|
||||
<system:String x:Key="customeQueryShortcutTitle">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="customeQueryShortcutTips">Enter a shortcut that automatically expands to the specified query.</system:String>
|
||||
<system:String x:Key="duplicateShortcut">Shortcut already exists, please enter a new Shortcut or edit the existing one.</system:String>
|
||||
<system:String x:Key="emptyShortcut">Shortcut and/or its expansion is empty.</system:String>
|
||||
|
||||
<!-- Hotkey Control -->
|
||||
<system:String x:Key="hotkeyUnavailable">Hotkey Unavailable</system:String>
|
||||
|
||||
|
|
@ -231,7 +308,7 @@
|
|||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Please wait...</system:String>
|
||||
|
||||
<!-- update -->
|
||||
<!-- Update -->
|
||||
<system:String x:Key="update_flowlauncher_update_check">Checking for new update</system:String>
|
||||
<system:String x:Key="update_flowlauncher_already_on_latest">You already have the latest Flow Launcher version</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_found">Update found</system:String>
|
||||
|
|
@ -271,7 +348,7 @@
|
|||
<system:String x:Key="HotkeyUpDownDesc">Back / Context Menu</system:String>
|
||||
<system:String x:Key="HotkeyLeftRightDesc">Item Navigation</system:String>
|
||||
<system:String x:Key="HotkeyShiftEnterDesc">Open Context Menu</system:String>
|
||||
<system:String x:Key="HotkeyCtrlEnterDesc">Open Contaning Folder</system:String>
|
||||
<system:String x:Key="HotkeyCtrlEnterDesc">Open Containing Folder</system:String>
|
||||
<system:String x:Key="HotkeyCtrlShiftEnterDesc">Run as Admin</system:String>
|
||||
<system:String x:Key="HotkeyCtrlHDesc">Query History</system:String>
|
||||
<system:String x:Key="HotkeyESCDesc">Back to Result in Context Menu</system:String>
|
||||
|
|
@ -284,7 +361,7 @@
|
|||
<system:String x:Key="RecommendWeatherDesc">Weather in Google Result</system:String>
|
||||
<system:String x:Key="RecommendShell">> ping 8.8.8.8</system:String>
|
||||
<system:String x:Key="RecommendShellDesc">Shell Command</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">s Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetoothDesc">Bluetooth in Windows Settings</system:String>
|
||||
<system:String x:Key="RecommendAcronyms">sn</system:String>
|
||||
<system:String x:Key="RecommendAcronymsDesc">Sticky Notes</system:String>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml version="1.0"?>
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">
|
||||
<!-- MainWindow -->
|
||||
<system:String x:Key="registerHotkeyFailed">Error al registrar la tecla de acceso directo: {0}</system:String>
|
||||
|
|
@ -16,14 +16,18 @@
|
|||
<system:String x:Key="copy">Copiar</system:String>
|
||||
<system:String x:Key="cut">Cortar</system:String>
|
||||
<system:String x:Key="paste">Pegar</system:String>
|
||||
<system:String x:Key="undo">Undo</system:String>
|
||||
<system:String x:Key="selectAll">Select All</system:String>
|
||||
<system:String x:Key="fileTitle">Archivo</system:String>
|
||||
<system:String x:Key="folderTitle">Carpeta</system:String>
|
||||
<system:String x:Key="textTitle">Texto</system:String>
|
||||
<system:String x:Key="GameMode">Modo de juego</system:String>
|
||||
<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>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Ajustes de Flow Launcher</system:String>
|
||||
<system:String x:Key="flowlauncher_settings">Ajustes</system:String>
|
||||
<system:String x:Key="general">General</system:String>
|
||||
<system:String x:Key="portableMode">Modo portable</system:String>
|
||||
<system:String x:Key="portableModeToolTIp">Almacena todos los ajustes y datos de usuario en una sola carpeta (útil cuando se utiliza con unidades extraíbles o servicios en la nube).</system:String>
|
||||
|
|
@ -31,7 +35,12 @@
|
|||
<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="rememberLastLocation">Recordar última ubicación de inicio</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Search Window Position</system:String>
|
||||
<system:String x:Key="SearchWindowPositionRememberLastLaunchLocation">Remember Last Position</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenter">Mouse Focused Screen - Center</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenterTop">Mouse Focused Screen - Center Top</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenLeftTop">Mouse Focused Screen - Left Top</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenRightTop">Mouse Focused Screen - Right Top</system:String>
|
||||
<system:String x:Key="language">Idioma</system:String>
|
||||
<system:String x:Key="lastQueryMode">Estilo de la última consulta</system:String>
|
||||
<system:String x:Key="lastQueryModeToolTip">Mostrar/Ocultar resultados anteriores cuando Flow Launcher es reactivado.</system:String>
|
||||
|
|
@ -39,25 +48,39 @@
|
|||
<system:String x:Key="LastQuerySelected">Seleccionar última consulta</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Borrar última consulta</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>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">Deshabilitar Flow Launcher cuando una aplicación de pantalla completa está activa (Recomendado para juegos).</system:String>
|
||||
<system:String x:Key="defaultFileManager">Gestor de archivos predeterminado</system:String>
|
||||
<system:String x:Key="defaultFileManagerToolTip">Seleccione el gestor de archivos a utilizar al abrir la carpeta.</system:String>
|
||||
<system:String x:Key="defaultBrowser">Navegador web predeterminado</system:String>
|
||||
<system:String x:Key="defaultBrowserToolTip">Configuración para Nueva Pestaña, Nueva Ventana, Modo Privado.</system:String>
|
||||
<system:String x:Key="pythonDirectory">Directorio de Python</system:String>
|
||||
<system:String x:Key="pythonFilePath">Python Path</system:String>
|
||||
<system:String x:Key="nodeFilePath">Node.js Path</system:String>
|
||||
<system:String x:Key="selectNodeExecutable">Please select the Node.js executable</system:String>
|
||||
<system:String x:Key="selectPythonExecutable">Please select pythonw.exe</system:String>
|
||||
<system:String x:Key="typingStartEn">Always Start Typing in English Mode</system:String>
|
||||
<system:String x:Key="typingStartEnTooltip">Temporarily change your input method to English mode when activating Flow.</system:String>
|
||||
<system:String x:Key="autoUpdates">Actualización automática</system:String>
|
||||
<system:String x:Key="selectPythonDirectory">Seleccionar</system:String>
|
||||
<system:String x:Key="select">Seleccionar</system:String>
|
||||
<system:String x:Key="hideOnStartup">Ocultar Flow Launcher al arrancar el sistema</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Ocultar icono de la bandeja</system:String>
|
||||
<system:String x:Key="hideNotifyIconToolTip">When the icon is hidden from the tray, the Settings menu can be opened by right-clicking on the search window.</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Precisión de la búsqueda</system:String>
|
||||
<system:String x:Key="querySearchPrecisionToolTip">Cambia la puntuación mínima de similitud requerida para resultados.</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Debe usar Pinyin</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">Permite el uso de Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizada para traducir chino</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="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>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="searchplugin">Search Plugin</system:String>
|
||||
<system:String x:Key="searchpluginToolTip">Ctrl+F to search plugins</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Title">No results found</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">Please try a different search.</system:String>
|
||||
<system:String x:Key="plugin">Plugin</system:String>
|
||||
<system:String x:Key="plugins">Plugins</system:String>
|
||||
<system:String x:Key="browserMorePlugins">Encontrar más plugins</system:String>
|
||||
<system:String x:Key="enable">Activado</system:String>
|
||||
<system:String x:Key="disable">Desactivado</system:String>
|
||||
|
|
@ -69,24 +92,47 @@
|
|||
<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>
|
||||
<system:String x:Key="priorityToolTip">Cambiar la prioridad del resultado del plugin</system:String>
|
||||
<system:String x:Key="pluginDirectory">Directorio de Plugins</system:String>
|
||||
<system:String x:Key="author">por</system:String>
|
||||
<system:String x:Key="plugin_init_time">Tiempo de inicio:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Tiempo de consulta:</system:String>
|
||||
<system:String x:Key="plugin_query_version">| Versión</system:String>
|
||||
<system:String x:Key="plugin_query_version">Versión</system:String>
|
||||
<system:String x:Key="plugin_query_web">Sitio web</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Uninstall</system:String>
|
||||
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Tienda de Plugins</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">Plugin</system:String>
|
||||
<system:String x:Key="pluginStore_Installed">Installed</system:String>
|
||||
<system:String x:Key="refresh">Recargar</system:String>
|
||||
<system:String x:Key="install">Instalar</system:String>
|
||||
<system:String x:Key="installbtn">Instalar</system:String>
|
||||
<system:String x:Key="uninstallbtn">Uninstall</system:String>
|
||||
<system:String x:Key="updatebtn">Update</system:String>
|
||||
<system:String x:Key="LabelInstalledToolTip">Plugin already installed</system:String>
|
||||
<system:String x:Key="LabelNew">New Version</system:String>
|
||||
<system:String x:Key="LabelNewToolTip">This plugin has been updated within the last 7 days</system:String>
|
||||
<system:String x:Key="LabelUpdateToolTip">New Update is Available</system:String>
|
||||
|
||||
|
||||
|
||||
<!-- Setting Theme -->
|
||||
<system:String x:Key="theme">Tema</system:String>
|
||||
<system:String x:Key="appearance">Appearance</system:String>
|
||||
<system:String x:Key="browserMoreThemes">Galería de Temas</system:String>
|
||||
<system:String x:Key="howToCreateTheme">Cómo crear un tema</system:String>
|
||||
<system:String x:Key="hiThere">Hola</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>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">Search the web with different search engine support</system:String>
|
||||
<system:String x:Key="SampleTitleProgram">Program</system:String>
|
||||
<system:String x:Key="SampleSubTitleProgram">Launch programs as admin or a different user</system:String>
|
||||
<system:String x:Key="SampleTitleProcessKiller">ProcessKiller</system:String>
|
||||
<system:String x:Key="SampleSubTitleProcessKiller">Terminate unwanted processes</system:String>
|
||||
<system:String x:Key="queryBoxFont">Fuente del cuadro de consulta</system:String>
|
||||
<system:String x:Key="resultItemFont">Fuente de los resultados</system:String>
|
||||
<system:String x:Key="windowMode">Modo Ventana</system:String>
|
||||
|
|
@ -103,27 +149,42 @@
|
|||
<system:String x:Key="SoundEffectTip">Reproducir un sonido al abrir la ventana de búsqueda</system:String>
|
||||
<system:String x:Key="Animation">Animación</system:String>
|
||||
<system:String x:Key="AnimationTip">Usar Animación en la Interfaz</system:String>
|
||||
<system:String x:Key="Clock">Clock</system:String>
|
||||
<system:String x:Key="Date">Date</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Tecla Rápida</system:String>
|
||||
<system:String x:Key="hotkeys">Tecla Rápida</system:String>
|
||||
<system:String x:Key="flowlauncherHotkey">Tecla de acceso a Flow Launcher</system:String>
|
||||
<system:String x:Key="flowlauncherHotkeyToolTip">Introduzca el acceso directo para mostrar/ocultar Flow Launcher.</system:String>
|
||||
<system:String x:Key="previewHotkey">Preview Hotkey</system:String>
|
||||
<system:String x:Key="previewHotkeyToolTip">Enter shortcut to show/hide preview in search window.</system:String>
|
||||
<system:String x:Key="openResultModifiers">Abrir Tecla de Modificación de Resultado</system:String>
|
||||
<system:String x:Key="openResultModifiersToolTip">Seleccione una tecla de modificación para abrir el resultado seleccionado vía teclado.</system:String>
|
||||
<system:String x:Key="showOpenResultHotkey">Mostrar tecla de acceso directo</system:String>
|
||||
<system:String x:Key="showOpenResultHotkeyToolTip">Mostrar tecla rápida de selección con resultados.</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Tecla Rápida de Consulta Personalizada</system:String>
|
||||
<system:String x:Key="customQueryShortcut">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="builtinShortcuts">Built-in Shortcut</system:String>
|
||||
<system:String x:Key="customQuery">Consulta</system:String>
|
||||
<system:String x:Key="customShortcut">Shortcut</system:String>
|
||||
<system:String x:Key="customShortcutExpansion">Expansion</system:String>
|
||||
<system:String x:Key="builtinShortcutDescription">Description</system:String>
|
||||
<system:String x:Key="delete">Eliminar</system:String>
|
||||
<system:String x:Key="edit">Editar</system:String>
|
||||
<system:String x:Key="add">Añadir</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Por favor, seleccione un elemento</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">¿Está seguro que desea eliminar la tecla de acceso directo del plugin {0}?</system:String>
|
||||
<system:String x:Key="deleteCustomShortcutWarning">Are you sure you want to delete shortcut: {0} with expansion {1}?</system:String>
|
||||
<system:String x:Key="shortcut_clipboard_description">Get text from clipboard.</system:String>
|
||||
<system:String x:Key="shortcut_active_explorer_path">Get path from active explorer.</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Efecto de sombra de ventana de búsqueda</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">El efecto sombra tiene un uso sustancial de GPU. No se recomienda si el rendimiento de su computadora es limitado.</system:String>
|
||||
<system:String x:Key="windowWidthSize">Tamaño de Ancho de Ventana</system:String>
|
||||
<system:String x:Key="windowWidthSizeToolTip">You can also quickly adjust this by using Ctrl+[ and Ctrl+].</system:String>
|
||||
<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>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">Proxy HTTP</system:String>
|
||||
|
|
@ -147,8 +208,10 @@
|
|||
<system:String x:Key="github">GitHub</system:String>
|
||||
<system:String x:Key="docs">Documentación</system:String>
|
||||
<system:String x:Key="version">Versión</system:String>
|
||||
<system:String x:Key="icons">Icons</system:String>
|
||||
<system:String x:Key="about_activate_times">Has activado Flow Launcher {0} veces</system:String>
|
||||
<system:String x:Key="checkUpdates">Buscar actualizaciones</system:String>
|
||||
<system:String x:Key="BecomeASponsor">Become A Sponsor</system:String>
|
||||
<system:String x:Key="newVersionTips">La nueva versión {0} está disponible, ¿desea reiniciar Flow Launcher para usar la actualización?</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">Falló la comprobación de actualizaciones, compruebe su conexión y configuración de proxy a api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
|
|
@ -160,6 +223,8 @@
|
|||
<system:String x:Key="devtool">Herramientas de desarrollo</system:String>
|
||||
<system:String x:Key="settingfolder">Carpeta de Configuración</system:String>
|
||||
<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="welcomewindow">Asistente</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
|
|
@ -207,6 +272,12 @@
|
|||
<system:String x:Key="invalidPluginHotkey">Tecla de acceso directo al plugin inválida</system:String>
|
||||
<system:String x:Key="update">Actualizar</system:String>
|
||||
|
||||
<!-- Custom Query Shortcut Dialog -->
|
||||
<system:String x:Key="customeQueryShortcutTitle">Custom Query Shortcut</system:String>
|
||||
<system:String x:Key="customeQueryShortcutTips">Enter a shortcut that automatically expands to the specified query.</system:String>
|
||||
<system:String x:Key="duplicateShortcut">Shortcut already exists, please enter a new Shortcut or edit the existing one.</system:String>
|
||||
<system:String x:Key="emptyShortcut">Shortcut and/or its expansion is empty.</system:String>
|
||||
|
||||
<!-- Hotkey Control -->
|
||||
<system:String x:Key="hotkeyUnavailable">Tecla No Disponible</system:String>
|
||||
|
||||
|
|
@ -229,7 +300,7 @@
|
|||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Por favor espere...</system:String>
|
||||
|
||||
<!-- update -->
|
||||
<!-- Update -->
|
||||
<system:String x:Key="update_flowlauncher_update_check">Buscando nueva actualización</system:String>
|
||||
<system:String x:Key="update_flowlauncher_already_on_latest">Ya esta instalada la última versión de Flow Launcher</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_found">Actualización encontrada</system:String>
|
||||
|
|
@ -282,7 +353,7 @@
|
|||
<system:String x:Key="RecommendWeatherDesc">Clima en los Resultados de Google</system:String>
|
||||
<system:String x:Key="RecommendShell">> ping 8.8.8.8</system:String>
|
||||
<system:String x:Key="RecommendShellDesc">Comando de Shell</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">s Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetoothDesc">Bluetooth en configuración de Windows</system:String>
|
||||
<system:String x:Key="RecommendAcronyms">sn</system:String>
|
||||
<system:String x:Key="RecommendAcronymsDesc">Notas adhesivas</system:String>
|
||||
|
|
|
|||
|
|
@ -16,14 +16,18 @@
|
|||
<system:String x:Key="copy">Copiar</system:String>
|
||||
<system:String x:Key="cut">Cortar</system:String>
|
||||
<system:String x:Key="paste">Pegar</system:String>
|
||||
<system:String x:Key="undo">Deshacer</system:String>
|
||||
<system:String x:Key="selectAll">Seleccionar todo</system:String>
|
||||
<system:String x:Key="fileTitle">Archivo</system:String>
|
||||
<system:String x:Key="folderTitle">Carpeta</system:String>
|
||||
<system:String x:Key="textTitle">Texto</system:String>
|
||||
<system:String x:Key="GameMode">Modo Juego</system:String>
|
||||
<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>
|
||||
|
||||
<!-- Setting General -->
|
||||
<system:String x:Key="flowlauncher_settings">Configuración de Flow Launcher</system:String>
|
||||
<system:String x:Key="flowlauncher_settings">Configuración</system:String>
|
||||
<system:String x:Key="general">General</system:String>
|
||||
<system:String x:Key="portableMode">Modo Portable</system:String>
|
||||
<system:String x:Key="portableModeToolTIp">Guarda toda la configuración y datos de usuario en una carpeta (Útil cuando se utiliza con unidades extraíbles o servicios en la nube).</system:String>
|
||||
|
|
@ -31,7 +35,12 @@
|
|||
<system:String x:Key="setAutoStartFailed">Error de configuración de arranque al iniciar</system:String>
|
||||
<system:String x:Key="hideFlowLauncherWhenLoseFocus">Ocultar Flow Launcher cuando se pierde el foco</system:String>
|
||||
<system:String x:Key="dontPromptUpdateMsg">No mostrar notificaciones de nuevas versiones</system:String>
|
||||
<system:String x:Key="rememberLastLocation">Recordar última posición de Flow Launcher</system:String>
|
||||
<system:String x:Key="SearchWindowPosition">Posición de la ventana de búsqueda</system:String>
|
||||
<system:String x:Key="SearchWindowPositionRememberLastLaunchLocation">Recordar última posición</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenter">Centro de la pantalla enfocada con el ratón</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenCenterTop">Arriba en el centro de la pantalla enfocada con el ratón</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenLeftTop">Arriba a la izquierda de la pantalla enfocada con el ratón</system:String>
|
||||
<system:String x:Key="SearchWindowPositionMouseScreenRightTop">Arriba a la derecha de la pantalla enfocada con el ratón</system:String>
|
||||
<system:String x:Key="language">Idioma</system:String>
|
||||
<system:String x:Key="lastQueryMode">Estilo de la última consulta</system:String>
|
||||
<system:String x:Key="lastQueryModeToolTip">Muestra/Oculta resultados anteriores cuando Flow Launcher es reactivado.</system:String>
|
||||
|
|
@ -39,25 +48,39 @@
|
|||
<system:String x:Key="LastQuerySelected">Seleccionar la última consulta</system:String>
|
||||
<system:String x:Key="LastQueryEmpty">Limpiar la última consulta</system:String>
|
||||
<system:String x:Key="maxShowResults">Número máximo de resultados mostrados</system:String>
|
||||
<system:String x:Key="maxShowResultsToolTip">También puede ajustarlo 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>
|
||||
<system:String x:Key="ignoreHotkeysOnFullscreenToolTip">Desactiva Flow Launcher cuando una aplicación de pantalla completa está activa (Recomendado para juegos).</system:String>
|
||||
<system:String x:Key="defaultFileManager">Administrador de archivos predeterminado</system:String>
|
||||
<system:String x:Key="defaultFileManagerToolTip">Selecciona el administrador de archivos que se desea utilizar para abrir la carpeta.</system:String>
|
||||
<system:String x:Key="defaultBrowser">Navegador web predeterminado</system:String>
|
||||
<system:String x:Key="defaultBrowserToolTip">Configuración para Nueva Pestaña, Nueva Ventana, Modo Privado.</system:String>
|
||||
<system:String x:Key="pythonDirectory">Carpeta de Python</system:String>
|
||||
<system:String x:Key="pythonFilePath">Ruta de Python</system:String>
|
||||
<system:String x:Key="nodeFilePath">Ruta de Node.js</system:String>
|
||||
<system:String x:Key="selectNodeExecutable">Seleccionar el ejecutable de Node.js</system:String>
|
||||
<system:String x:Key="selectPythonExecutable">Seleccionar pythonw.exe</system:String>
|
||||
<system:String x:Key="typingStartEn">Empezar a escribir siempre en modo inglés</system:String>
|
||||
<system:String x:Key="typingStartEnTooltip">Cambia temporalmente el método de entrada al modo inglés cuando se activa Flow.</system:String>
|
||||
<system:String x:Key="autoUpdates">Actualización automática</system:String>
|
||||
<system:String x:Key="selectPythonDirectory">Seleccionar</system:String>
|
||||
<system:String x:Key="select">Seleccionar</system:String>
|
||||
<system:String x:Key="hideOnStartup">Ocultar Flow Launcher al inicio</system:String>
|
||||
<system:String x:Key="hideNotifyIcon">Ocultar icono de la bandeja del sistema</system:String>
|
||||
<system:String x:Key="hideNotifyIconToolTip">Cuando el icono está oculto en la bandeja del sistema, se puede abrir el menú de configuración haciendo clic con el botón derecho en la ventana de búsqueda.</system:String>
|
||||
<system:String x:Key="querySearchPrecision">Precisión en la búsqueda de consultas</system:String>
|
||||
<system:String x:Key="querySearchPrecisionToolTip">Cambia la puntuación mínima requerida para la coincidencia de los resultados.</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Utilizar Pinyin</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">Permite utilizar Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizado para traducir chino</system:String>
|
||||
<system:String x:Key="ShouldUsePinyin">Buscar con Pinyin</system:String>
|
||||
<system:String x:Key="ShouldUsePinyinToolTip">Permite utilizar Pinyin para la búsqueda. Pinyin es el sistema estándar de ortografía romanizado para traducir chino.</system:String>
|
||||
<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. Pulse {0} para mostrar/ocultar la vista previa.</system:String>
|
||||
<system:String x:Key="shadowEffectNotAllowed">El efecto de sombra no está permitido mientras el tema actual tenga el efecto de desenfoque activado</system:String>
|
||||
|
||||
<!-- Setting Plugin -->
|
||||
<system:String x:Key="plugin">Complemento</system:String>
|
||||
<system:String x:Key="searchplugin">Buscar complemento</system:String>
|
||||
<system:String x:Key="searchpluginToolTip">Ctrl+F para buscar complementos</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Title">No se han encontrado resultados</system:String>
|
||||
<system:String x:Key="searchplugin_Noresult_Subtitle">Por favor, intente una búsqueda diferente.</system:String>
|
||||
<system:String x:Key="plugin">Complementos</system:String>
|
||||
<system:String x:Key="plugins">Complementos</system:String>
|
||||
<system:String x:Key="browserMorePlugins">Buscar más complementos</system:String>
|
||||
<system:String x:Key="enable">Activado</system:String>
|
||||
<system:String x:Key="disable">Desactivado</system:String>
|
||||
|
|
@ -65,35 +88,58 @@
|
|||
<system:String x:Key="actionKeywords">Palabra clave de acción</system:String>
|
||||
<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 las palabras clave de acción</system:String>
|
||||
<system:String x:Key="actionKeywordsTooltip">Cambia la palabra clave de acción</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>
|
||||
<system:String x:Key="pluginDirectory">Carpeta de complementos</system:String>
|
||||
<system:String x:Key="priorityToolTip">Cambiar la prioridad de los resultados del complemento</system:String>
|
||||
<system:String x:Key="pluginDirectory">Carpeta del complemento</system:String>
|
||||
<system:String x:Key="author">por</system:String>
|
||||
<system:String x:Key="plugin_init_time">Tiempo de inicio:</system:String>
|
||||
<system:String x:Key="plugin_query_time">Tiempo de consulta:</system:String>
|
||||
<system:String x:Key="plugin_query_version">| Versión</system:String>
|
||||
<system:String x:Key="plugin_query_version">Versión</system:String>
|
||||
<system:String x:Key="plugin_query_web">Sitio web</system:String>
|
||||
<system:String x:Key="plugin_uninstall">Desinstalar</system:String>
|
||||
|
||||
|
||||
<!-- Setting Plugin Store -->
|
||||
<system:String x:Key="pluginStore">Tienda de complementos</system:String>
|
||||
<system:String x:Key="pluginStore_NewRelease">Nuevo lanzamiento</system:String>
|
||||
<system:String x:Key="pluginStore_RecentlyUpdated">Actualizado recientemente</system:String>
|
||||
<system:String x:Key="pluginStore_None">Complementos</system:String>
|
||||
<system:String x:Key="pluginStore_Installed">Instalado</system:String>
|
||||
<system:String x:Key="refresh">Refrescar</system:String>
|
||||
<system:String x:Key="install">Instalar</system:String>
|
||||
<system:String x:Key="installbtn">Instalar</system:String>
|
||||
<system:String x:Key="uninstallbtn">Desinstalar</system:String>
|
||||
<system:String x:Key="updatebtn">Actualizar</system:String>
|
||||
<system:String x:Key="LabelInstalledToolTip">Complemento ya instalado</system:String>
|
||||
<system:String x:Key="LabelNew">Nueva versión</system:String>
|
||||
<system:String x:Key="LabelNewToolTip">Este complemento ha sido actualizado en los últimos 7 días</system:String>
|
||||
<system:String x:Key="LabelUpdateToolTip">Nueva actualización disponible</system:String>
|
||||
|
||||
|
||||
|
||||
<!-- Setting Theme -->
|
||||
<system:String x:Key="theme">Tema</system:String>
|
||||
<system:String x:Key="appearance">Apariencia</system:String>
|
||||
<system:String x:Key="browserMoreThemes">Galería de temas</system:String>
|
||||
<system:String x:Key="howToCreateTheme">Cómo crear un tema</system:String>
|
||||
<system:String x:Key="hiThere">Hola</system:String>
|
||||
<system:String x:Key="SampleTitleExplorer">Explorador</system:String>
|
||||
<system:String x:Key="SampleSubTitleExplorer">Buscar archivos, carpetas y contenido de archivos</system:String>
|
||||
<system:String x:Key="SampleTitleWebSearch">Búsqueda Web</system:String>
|
||||
<system:String x:Key="SampleSubTitleWebSearch">Buscar en la web con el apoyo de diferentes motores de búsqueda</system:String>
|
||||
<system:String x:Key="SampleTitleProgram">Programa</system:String>
|
||||
<system:String x:Key="SampleSubTitleProgram">Iniciar programas como administrador o como usuario diferente</system:String>
|
||||
<system:String x:Key="SampleTitleProcessKiller">Eliminar Procesos</system:String>
|
||||
<system:String x:Key="SampleSubTitleProcessKiller">Terminar procesos no deseados</system:String>
|
||||
<system:String x:Key="queryBoxFont">Fuente del texto del cuadro de consulta</system:String>
|
||||
<system:String x:Key="resultItemFont">Fuente del texto de los resultados</system:String>
|
||||
<system:String x:Key="windowMode">Modo Ventana</system:String>
|
||||
<system:String x:Key="opacity">Opacidad</system:String>
|
||||
<system:String x:Key="theme_load_failure_path_not_exists">El tema {0} no existe, activando el tema por defecto</system:String>
|
||||
<system:String x:Key="theme_load_failure_parse_error">Fallo al cargar el tema {0}, activando el tema predeterminado</system:String>
|
||||
<system:String x:Key="ThemeFolder">Carpeta de temas</system:String>
|
||||
<system:String x:Key="ThemeFolder">Carpeta de temas del usuario</system:String>
|
||||
<system:String x:Key="OpenThemeFolder">Abrir carpeta de temas</system:String>
|
||||
<system:String x:Key="ColorScheme">Esquema de colores</system:String>
|
||||
<system:String x:Key="ColorSchemeSystem">Predeterminado del sistema</system:String>
|
||||
|
|
@ -103,27 +149,42 @@
|
|||
<system:String x:Key="SoundEffectTip">Reproduce un pequeño sonido cuando se abre el cuadro de búsqueda</system:String>
|
||||
<system:String x:Key="Animation">Animación</system:String>
|
||||
<system:String x:Key="AnimationTip">Usar animación en la Interfaz de Usuario</system:String>
|
||||
<system:String x:Key="Clock">Reloj</system:String>
|
||||
<system:String x:Key="Date">Fecha</system:String>
|
||||
|
||||
<!-- Setting Hotkey -->
|
||||
<system:String x:Key="hotkey">Atajo de teclado</system:String>
|
||||
<system:String x:Key="hotkey">Atajos de teclado</system:String>
|
||||
<system:String x:Key="hotkeys">Atajos de teclado</system:String>
|
||||
<system:String x:Key="flowlauncherHotkey">Atajo de teclado de Flow Launcher</system:String>
|
||||
<system:String x:Key="flowlauncherHotkeyToolTip">Introduzca el atajo de teclado para mostrar/ocultar Flow Launcher.</system:String>
|
||||
<system:String x:Key="previewHotkey">Atajo de teclado para vista previa</system:String>
|
||||
<system:String x:Key="previewHotkeyToolTip">Introduzca el acceso directo para mostrar/ocultar la vista previa en la ventana de búsqueda.</system:String>
|
||||
<system:String x:Key="openResultModifiers">Tecla modificadora para abrir resultado</system:String>
|
||||
<system:String x:Key="openResultModifiersToolTip">Seleccione una tecla modificadora para abrir el resultado seleccionado con el teclado.</system:String>
|
||||
<system:String x:Key="showOpenResultHotkey">Mostrar atajo de teclado</system:String>
|
||||
<system:String x:Key="showOpenResultHotkeyToolTip">Muestra atajo de teclado de selección junto a los resultados.</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Atajo de teclado de consulta personalizada</system:String>
|
||||
<system:String x:Key="customQueryHotkey">Atajos de teclado de consulta personalizada</system:String>
|
||||
<system:String x:Key="customQueryShortcut">Accesos directos de consulta personalizada</system:String>
|
||||
<system:String x:Key="builtinShortcuts">Accesos directos integrados</system:String>
|
||||
<system:String x:Key="customQuery">Consulta</system:String>
|
||||
<system:String x:Key="customShortcut">Acceso directo</system:String>
|
||||
<system:String x:Key="customShortcutExpansion">Expansión</system:String>
|
||||
<system:String x:Key="builtinShortcutDescription">Descripción</system:String>
|
||||
<system:String x:Key="delete">Eliminar</system:String>
|
||||
<system:String x:Key="edit">Editar</system:String>
|
||||
<system:String x:Key="add">Añadir</system:String>
|
||||
<system:String x:Key="pleaseSelectAnItem">Por favor, seleccione un elemento</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">¿Está seguro que desea eliminar el atajo de teclado del complemento {0}?</system:String>
|
||||
<system:String x:Key="deleteCustomHotkeyWarning">¿Está seguro que desea eliminar el atajo de teclado de consulta personalizada {0}?</system:String>
|
||||
<system:String x:Key="deleteCustomShortcutWarning">¿Está seguro que desea eliminar el acceso directo: {0} con la expansión {1}?</system:String>
|
||||
<system:String x:Key="shortcut_clipboard_description">Obtiene el texto del portapapeles.</system:String>
|
||||
<system:String x:Key="shortcut_active_explorer_path">Obtiene la ruta del explorador activo.</system:String>
|
||||
<system:String x:Key="queryWindowShadowEffect">Efecto de sombra de la ventana de consultas</system:String>
|
||||
<system:String x:Key="shadowEffectCPUUsage">El efecto de sombra hace un uso sustancial de la GPU. No se recomienda para ordenadores de rendimiento limitado.</system:String>
|
||||
<system:String x:Key="windowWidthSize">Tamaño del ancho de la ventana</system:String>
|
||||
<system:String x:Key="windowWidthSizeToolTip">También puede ajustarlo rápidamente usando Ctrl+[y Ctrl+].</system:String>
|
||||
<system:String x:Key="useGlyphUI">Usar iconos Segoe Fluent</system:String>
|
||||
<system:String x:Key="useGlyphUIEffect">Usa iconos Segoe Fluent para los resultados de la consulta cuando sean compatibles</system:String>
|
||||
<system:String x:Key="flowlauncherPressHotkey">Pulsar Tecla</system:String>
|
||||
|
||||
<!-- Setting Proxy -->
|
||||
<system:String x:Key="proxy">Proxy HTTP</system:String>
|
||||
|
|
@ -147,8 +208,10 @@
|
|||
<system:String x:Key="github">GitHub</system:String>
|
||||
<system:String x:Key="docs">Documentación</system:String>
|
||||
<system:String x:Key="version">Versión</system:String>
|
||||
<system:String x:Key="icons">Iconos</system:String>
|
||||
<system:String x:Key="about_activate_times">Ha activado Flow Launcher {0} veces</system:String>
|
||||
<system:String x:Key="checkUpdates">Buscar actualizaciones</system:String>
|
||||
<system:String x:Key="BecomeASponsor">Hágase Patrocinador</system:String>
|
||||
<system:String x:Key="newVersionTips">La nueva versión {0} está disponible, ¿desea reiniciar Flow Launcher para actualizar?</system:String>
|
||||
<system:String x:Key="checkUpdatesFailed">La comprobación de las actualizaciones ha fallado, por favor, compruebe la configuración de su proxy y conexión a api.github.com.</system:String>
|
||||
<system:String x:Key="downloadUpdatesFailed">
|
||||
|
|
@ -157,9 +220,11 @@
|
|||
</system:String>
|
||||
<system:String x:Key="releaseNotes">Notas de la versión</system:String>
|
||||
<system:String x:Key="documentation">Consejos de uso</system:String>
|
||||
<system:String x:Key="devtool">Herramientas de desarrolador</system:String>
|
||||
<system:String x:Key="devtool">Herramientas de desarrollador</system:String>
|
||||
<system:String x:Key="settingfolder">Carpeta de configuración</system:String>
|
||||
<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 que desea eliminar todos los registros?</system:String>
|
||||
<system:String x:Key="welcomewindow">Asistente</system:String>
|
||||
|
||||
<!-- FileManager Setting Dialog -->
|
||||
|
|
@ -184,7 +249,7 @@
|
|||
|
||||
<!-- Priority Setting Dialog -->
|
||||
<system:String x:Key="changePriorityWindow">Cambiar la prioridad</system:String>
|
||||
<system:String x:Key="priority_tips">A mayor número, más alto aparecerá el resultado. Inténtelo con 5. Si quiere que los resultados aparezcan más abajo que los de cualquier otro complemento, utilice un número negativo</system:String>
|
||||
<system:String x:Key="priority_tips">Cuanto mayor sea el número, más arriba se situará el resultado. Inténtelo con 5. Si desea que los resultados se situén más abajo que los de cualquier otro complemento, utilice un número negativo</system:String>
|
||||
<system:String x:Key="invalidPriority">¡Por favor, proporcione un número entero válido para la prioridad!</system:String>
|
||||
|
||||
<!-- Action Keyword Setting Dialog -->
|
||||
|
|
@ -201,12 +266,18 @@
|
|||
|
||||
<!-- Custom Query Hotkey Dialog -->
|
||||
<system:String x:Key="customeQueryHotkeyTitle">Atajo de teclado de consulta personalizada</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Pulse el atajo de teclado personalizado para insertar automáticamente la consulta especificada.</system:String>
|
||||
<system:String x:Key="customeQueryHotkeyTips">Pulse el atajo de teclado personalizado para abrir Flow Launcher y realizar automáticamente la consulta especificada.</system:String>
|
||||
<system:String x:Key="preview">Vista previa</system:String>
|
||||
<system:String x:Key="hotkeyIsNotUnavailable">El atajo de teclado no está disponible, por favor seleccione uno nuevo</system:String>
|
||||
<system:String x:Key="invalidPluginHotkey">Atajo de teclado de complemento no válido</system:String>
|
||||
<system:String x:Key="update">Actualizar</system:String>
|
||||
|
||||
<!-- Custom Query Shortcut Dialog -->
|
||||
<system:String x:Key="customeQueryShortcutTitle">Acceso directo de consulta personalizada</system:String>
|
||||
<system:String x:Key="customeQueryShortcutTips">Introduzca un acceso directo para expandir automáticamente la consulta especificada.</system:String>
|
||||
<system:String x:Key="duplicateShortcut">El acceso directo ya existe, por favor introduzca uno nuevo o edite el existente.</system:String>
|
||||
<system:String x:Key="emptyShortcut">El acceso directo y/o su expansión están vacíos.</system:String>
|
||||
|
||||
<!-- Hotkey Control -->
|
||||
<system:String x:Key="hotkeyUnavailable">Atajo de teclado no disponible</system:String>
|
||||
|
||||
|
|
@ -229,7 +300,7 @@
|
|||
<!-- General Notice -->
|
||||
<system:String x:Key="pleaseWait">Por favor espere...</system:String>
|
||||
|
||||
<!-- update -->
|
||||
<!-- Update -->
|
||||
<system:String x:Key="update_flowlauncher_update_check">Comprobando actualizaciones</system:String>
|
||||
<system:String x:Key="update_flowlauncher_already_on_latest">Ya tiene la última versión de Flow Launcher</system:String>
|
||||
<system:String x:Key="update_flowlauncher_update_found">Actualización encontrada</system:String>
|
||||
|
|
@ -262,7 +333,7 @@
|
|||
<system:String x:Key="Welcome_Page4_Title">Palabra clave de acción y comandos</system:String>
|
||||
<system:String x:Key="Welcome_Page4_Text01">Busque en la web, inicie aplicaciones o ejecute diversas funciones mediante los complementos de Flow Launcher. Algunas funciones comienzan con una palabra clave de acción y, si es necesario, pueden utilizarse sin ellas. Pruebe las siguientes consultas en Flow Launcher.</system:String>
|
||||
<system:String x:Key="Welcome_Page5_Title">Iniciemos Flow Launcher</system:String>
|
||||
<system:String x:Key="Welcome_Page5_Text01">Terminado. Disfruta de Flow Launcher. No olvides el atajo de teclado para empezar :)</system:String>
|
||||
<system:String x:Key="Welcome_Page5_Text01">Finalizado. Disfrute de Flow Launcher. No olvide utilizar el atajo de teclado para iniciar :)</system:String>
|
||||
|
||||
<!-- General Guide & Hotkey -->
|
||||
|
||||
|
|
@ -282,7 +353,7 @@
|
|||
<system:String x:Key="RecommendWeatherDesc">El tiempo en los resultados de Google</system:String>
|
||||
<system:String x:Key="RecommendShell">> ping 8.8.8.8</system:String>
|
||||
<system:String x:Key="RecommendShellDesc">Comando de terminal</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetooth">s Bluetooth</system:String>
|
||||
<system:String x:Key="RecommendBluetoothDesc">Bluetooth en la configuración de Windows</system:String>
|
||||
<system:String x:Key="RecommendAcronyms">sn</system:String>
|
||||
<system:String x:Key="RecommendAcronymsDesc">Notas adhesivas</system:String>
|
||||
|
|
|
|||