feat: add .desktop entry
This commit is contained in:
parent
75b0ea38ae
commit
a64eabcfd4
vendor/fyne.io/fyne/v2
.gitignore.godocdown.importAUTHORSCHANGELOG.mdCODE_OF_CONDUCT.mdCONTRIBUTING.mdLICENSEREADME.mdSECURITY.mdanimation.goapp.go
app
app.goapp_darwin.goapp_darwin.mapp_debug.goapp_desktop_darwin.goapp_desktop_darwin.mapp_gl.goapp_goxjs.goapp_mobile.goapp_mobile_and.capp_mobile_and.goapp_mobile_ios.goapp_mobile_ios.mapp_notlegacy_darwin.goapp_openurl_js.goapp_openurl_wasm.goapp_openurl_web.goapp_other.goapp_release.goapp_software.goapp_standard.goapp_theme_js.goapp_theme_wasm.goapp_theme_web.goapp_windows.goapp_xdg.gocloud.gometa.gopreferences.gopreferences_android.gopreferences_ios.gopreferences_mobile.gopreferences_other.gosettings.gosettings_desktop.gosettings_file.gosettings_goxjs.gosettings_mobile.gosettings_noanimation.gostorage.go
canvas.gocanvas
canvasobject.goclipboard.gocloud.gocontainer.gocontainer
data/binding
binding.gobinditems.gobindlists.gobindtrees.gobool.gocomparator_helper.goconvert.goconvert_helper.golistbinding.gomapbinding.gopref_helper.gopreference.goqueue.gosprintf.gotreebinding.go
device.godriver.godriver
54
vendor/fyne.io/fyne/v2/.gitignore
generated
vendored
Normal file
54
vendor/fyne.io/fyne/v2/.gitignore
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
### Project Specific
|
||||
cmd/fyne/fyne
|
||||
cmd/fyne/fyne.exe
|
||||
cmd/fyne_demo/fyne_demo
|
||||
cmd/fyne_demo/fyne_demo.apk
|
||||
cmd/fyne_demo/fyne-demo.app
|
||||
cmd/fyne_demo/fyne_demo.exe
|
||||
cmd/fyne_settings/fyne_settings
|
||||
cmd/fyne_settings/fyne_settings.apk
|
||||
cmd/fyne_settings/fyne_settings.app
|
||||
cmd/fyne_settings/fyne_settings.exe
|
||||
cmd/hello/hello
|
||||
cmd/hello/hello.apk
|
||||
cmd/hello/hello.app
|
||||
cmd/hello/hello.exe
|
||||
fyne-cross
|
||||
|
||||
### Tests
|
||||
**/testdata/failed
|
||||
|
||||
### Go
|
||||
# Output of the coverage tool
|
||||
*.out
|
||||
|
||||
### macOS
|
||||
# General
|
||||
.DS_Store
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
### JetBrains
|
||||
.idea
|
||||
|
||||
### VSCode
|
||||
.vscode
|
||||
|
||||
### Vim
|
||||
# Swap
|
||||
[._]*.s[a-v][a-z]
|
||||
[._]*.sw[a-p]
|
||||
[._]s[a-v][a-z]
|
||||
[._]sw[a-p]
|
||||
|
||||
# Session
|
||||
Session.vim
|
||||
|
||||
# Temporary
|
||||
.netrwhist
|
||||
*~
|
||||
# Auto-generated tag files
|
||||
tags
|
||||
# Persistent undo
|
||||
[._]*.un~
|
1
vendor/fyne.io/fyne/v2/.godocdown.import
generated
vendored
Normal file
1
vendor/fyne.io/fyne/v2/.godocdown.import
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
fyne.io/fyne/v2
|
14
vendor/fyne.io/fyne/v2/AUTHORS
generated
vendored
Normal file
14
vendor/fyne.io/fyne/v2/AUTHORS
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
Andy Williams <andy@andy.xyz>
|
||||
Steve OConnor <steveoc64@gmail.com>
|
||||
Luca Corbo <lu.corbo@gmail.com>
|
||||
Paul Hovey <paul@paulhovey.org>
|
||||
Charles Corbett <nafredy@gmail.com>
|
||||
Tilo Prütz <tilo@pruetz.net>
|
||||
Stephen Houston <smhouston88@gmail.com>
|
||||
Storm Hess <stormhess@gloryskulls.com>
|
||||
Stuart Scott <stuart.murray.scott@gmail.com>
|
||||
Jacob Alzén <jacalz@tutanota.com>
|
||||
Charles A. Daniels <charles@cdaniels.net>
|
||||
Pablo Fuentes <f.pablo1@hotmail.com>
|
||||
Changkun Ou <hi@changkun.de>
|
||||
|
1316
vendor/fyne.io/fyne/v2/CHANGELOG.md
generated
vendored
Normal file
1316
vendor/fyne.io/fyne/v2/CHANGELOG.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
76
vendor/fyne.io/fyne/v2/CODE_OF_CONDUCT.md
generated
vendored
Normal file
76
vendor/fyne.io/fyne/v2/CODE_OF_CONDUCT.md
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||
level of experience, education, socio-economic status, nationality, personal
|
||||
appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at info@fyne.io. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see
|
||||
https://www.contributor-covenant.org/faq
|
63
vendor/fyne.io/fyne/v2/CONTRIBUTING.md
generated
vendored
Normal file
63
vendor/fyne.io/fyne/v2/CONTRIBUTING.md
generated
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
Thanks very much for your interest in contributing to Fyne!
|
||||
The community is what makes this project successful and we are glad to welcome you on board.
|
||||
|
||||
There are various ways to contribute, perhaps the following helps you know how to get started.
|
||||
|
||||
## Reporting a bug
|
||||
|
||||
If you've found something wrong we want to know about it, please help us understand the problem so we can resolve it.
|
||||
|
||||
1. Check to see if this already is recorded, if so add some more information [issue list](https://github.com/fyne-io/fyne/issues)
|
||||
2. If not then create a new issue using the [bug report template](https://github.com/fyne-io/fyne/issues/new?assignees=&labels=&template=bug_report.md&title=)
|
||||
3. Stay involved in the conversation on the issue as it is triaged and progressed.
|
||||
|
||||
|
||||
## Fixing an issue
|
||||
|
||||
Great! You found an issue and figured you can fix it for us.
|
||||
If you can follow these steps then your code should get accepted fast.
|
||||
|
||||
1. Read through the "Contributing Code" section further down this page.
|
||||
2. Write a unit test to show it is broken.
|
||||
3. Create the fix and you should see the test passes.
|
||||
4. Run the tests and make sure everything still works as expected using `go test ./...`.
|
||||
5. [Open a PR](https://github.com/fyne-io/fyne/compare) and work through the review checklist.
|
||||
|
||||
|
||||
## Adding a feature
|
||||
|
||||
It's always good news to hear that people want to contribute functionality.
|
||||
But first of all check that it fits within our [Vision](https://github.com/fyne-io/fyne/wiki/Vision) and if we are already considering it on our [Roadmap](https://github.com/fyne-io/fyne/wiki/Roadmap).
|
||||
If you're not sure then you should join our #fyne-contributors channel on the [Gophers Slack server](https://gophers.slack.com/app_redirect?channel=fyne-contributors).
|
||||
|
||||
Once you are ready to code then the following steps should give you a smooth process:
|
||||
|
||||
1. Read through the [Contributing Code](#contributing-code) section further down this page.
|
||||
2. Think about how you would structure your code and how it can be tested.
|
||||
3. Write some code and enjoy the ease of writing Go code for even a complex project :).
|
||||
4. Run the tests and make sure everything still works as expected using `go test ./...`.
|
||||
5. [Open a PR](https://github.com/fyne-io/fyne/compare) and work through the review checklist.
|
||||
|
||||
|
||||
# Contributing Code
|
||||
|
||||
We aim to maintain a very high standard of code, through design, test and implementation.
|
||||
To manage this we have various checks and processes in place that everyone should follow, including:
|
||||
|
||||
* We use the Go standard format (with tabs not spaces) - you can run `gofmt` before committing
|
||||
* Imports should be ordered according to the GoImports spec - you can use the `goimports` tool instead of `gofmt`.
|
||||
* Everything should have a unit test attached (as much as possible, to keep our coverage up)
|
||||
|
||||
For detailed Code style, check [Contributing](https://github.com/fyne-io/fyne/wiki/Contributing#code-style) in our wiki please.
|
||||
|
||||
# Decision Process
|
||||
|
||||
The following points apply to our decision making process:
|
||||
|
||||
* Any decisions or votes will be opened on the #fyne-contributors channel and follows lazy consensus.
|
||||
* Any contributors not responding in 4 days will be deemed in agreement.
|
||||
* Any PR that has not been responded to within 7 days can be automatically approved.
|
||||
* No functionality will be added unless at least 2 developers agree it belongs.
|
||||
|
||||
Bear in mind that this is a cross platform project so any new features would normally
|
||||
be required to work on multiple desktop and mobile platforms.
|
28
vendor/fyne.io/fyne/v2/LICENSE
generated
vendored
Normal file
28
vendor/fyne.io/fyne/v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (C) 2018 Fyne.io developers (see AUTHORS)
|
||||
All rights reserved.
|
||||
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Fyne.io nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
188
vendor/fyne.io/fyne/v2/README.md
generated
vendored
Normal file
188
vendor/fyne.io/fyne/v2/README.md
generated
vendored
Normal file
@ -0,0 +1,188 @@
|
||||
<p align="center">
|
||||
<a href="https://pkg.go.dev/fyne.io/fyne/v2?tab=doc" title="Go API Reference" rel="nofollow"><img src="https://img.shields.io/badge/go-documentation-blue.svg?style=flat" alt="Go API Reference"></a>
|
||||
<a href="https://img.shields.io/github/v/release/fyne-io/fyne?include_prereleases" title="Latest Release" rel="nofollow"><img src="https://img.shields.io/github/v/release/fyne-io/fyne?include_prereleases" alt="Latest Release"></a>
|
||||
<a href='https://gophers.slack.com/messages/fyne'><img src='https://img.shields.io/badge/join-us%20on%20slack-gray.svg?longCache=true&logo=slack&colorB=blue' alt='Join us on Slack' /></a>
|
||||
<br />
|
||||
<a href="https://goreportcard.com/report/fyne.io/fyne/v2"><img src="https://goreportcard.com/badge/fyne.io/fyne/v2" alt="Code Status" /></a>
|
||||
<a href="https://github.com/fyne-io/fyne/actions"><img src="https://github.com/fyne-io/fyne/workflows/Platform%20Tests/badge.svg" alt="Build Status" /></a>
|
||||
<a href='https://coveralls.io/github/fyne-io/fyne?branch=develop'><img src='https://coveralls.io/repos/github/fyne-io/fyne/badge.svg?branch=develop' alt='Coverage Status' /></a>
|
||||
</p>
|
||||
|
||||
# About
|
||||
|
||||
[Fyne](https://fyne.io) is an easy-to-use UI toolkit and app API written in Go.
|
||||
It is designed to build applications that run on desktop and mobile devices with a
|
||||
single codebase.
|
||||
|
||||
Version 2.4 is the current release of the Fyne API, it added rounded corners, emoji,
|
||||
layout debug support and table headers, along with a large number of
|
||||
smaller feature additions.
|
||||
We are now working towards the next big release, codenamed
|
||||
[Elgin](https://github.com/fyne-io/fyne/milestone/21)
|
||||
and more news will follow in our news feeds and GitHub project.
|
||||
|
||||
# Prerequisites
|
||||
|
||||
To develop apps using Fyne you will need Go version 1.17 or later, a C compiler and your system's development tools.
|
||||
If you're not sure if that's all installed or you don't know how then check out our
|
||||
[Getting Started](https://fyne.io/develop/) document.
|
||||
|
||||
Using the standard go tools you can install Fyne's core library using:
|
||||
|
||||
go get fyne.io/fyne/v2@latest
|
||||
|
||||
After importing a new module, run the following command before compiling the code for the first time. Avoid running it before writing code that uses the module to prevent accidental removal of dependencies:
|
||||
|
||||
go mod tidy
|
||||
|
||||
# Widget demo
|
||||
|
||||
To run a showcase of the features of Fyne execute the following:
|
||||
|
||||
go install fyne.io/fyne/v2/cmd/fyne_demo@latest
|
||||
fyne_demo
|
||||
|
||||
And you should see something like this (after you click a few buttons):
|
||||
|
||||
<p align="center" markdown="1" style="max-width: 100%">
|
||||
<img src="img/widgets-dark.png" width="752" alt="Fyne Demo Dark Theme" style="max-width: 100%" />
|
||||
</p>
|
||||
|
||||
Or if you are using the light theme:
|
||||
|
||||
<p align="center" markdown="1" style="max-width: 100%">
|
||||
<img src="img/widgets-light.png" width="752" alt="Fyne Demo Light Theme" style="max-width: 100%" />
|
||||
</p>
|
||||
|
||||
And even running on a mobile device:
|
||||
|
||||
<p align="center" markdown="1" style="max-width: 100%">
|
||||
<img src="img/widgets-mobile-light.png" width="348" alt="Fyne Demo Mobile Light Theme" style="max-width: 100%" />
|
||||
</p>
|
||||
|
||||
# Getting Started
|
||||
|
||||
Fyne is designed to be really easy to code with.
|
||||
If you have followed the prerequisite steps above then all you need is a
|
||||
Go IDE (or a text editor).
|
||||
|
||||
Open a new file and you're ready to write your first app!
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2/app"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
func main() {
|
||||
a := app.New()
|
||||
w := a.NewWindow("Hello")
|
||||
|
||||
hello := widget.NewLabel("Hello Fyne!")
|
||||
w.SetContent(container.NewVBox(
|
||||
hello,
|
||||
widget.NewButton("Hi!", func() {
|
||||
hello.SetText("Welcome :)")
|
||||
}),
|
||||
))
|
||||
|
||||
w.ShowAndRun()
|
||||
}
|
||||
```
|
||||
|
||||
And you can run that simply as:
|
||||
|
||||
go run main.go
|
||||
|
||||
It should look like this:
|
||||
|
||||
<div align="center">
|
||||
<table cellpadding="0" cellspacing="0" style="margin: auto; border-collapse: collapse;">
|
||||
<tr style="border: none;"><td style="border: none;">
|
||||
<img src="img/hello-light.png" width="207" alt="Fyne Hello Dark Theme" />
|
||||
</td><td style="border: none;">
|
||||
<img src="img/hello-dark.png" width="207" alt="Fyne Hello Dark Theme" />
|
||||
</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
## Run in mobile simulation
|
||||
|
||||
There is a helpful mobile simulation mode that gives a hint of how your app would work on a mobile device:
|
||||
|
||||
go run -tags mobile main.go
|
||||
|
||||
Another option is to use `fyne` command, see [Packaging for mobile](#packaging-for-mobile).
|
||||
|
||||
# Installing
|
||||
|
||||
Using `go install` will copy the executable into your go `bin` dir.
|
||||
To install the application with icons etc into your operating system's standard
|
||||
application location you can use the fyne utility and the "install" subcommand.
|
||||
|
||||
go install fyne.io/fyne/v2/cmd/fyne@latest
|
||||
fyne install
|
||||
|
||||
# Packaging for mobile
|
||||
|
||||
To run on a mobile device it is necessary to package up the application.
|
||||
To do this we can use the fyne utility "package" subcommand.
|
||||
You will need to add appropriate parameters as prompted, but the basic command is shown below.
|
||||
Once packaged you can install using the platform development tools or the fyne "install" subcommand.
|
||||
|
||||
fyne package -os android -appID my.domain.appname
|
||||
fyne install -os android
|
||||
|
||||
The built Android application can run either in a real device or an Android emulator.
|
||||
However, building for iOS is slightly different.
|
||||
If the "-os" argument is "ios", it is build only for a real iOS device.
|
||||
Specify "-os" to "iossimulator" allows the application be able to run in an iOS simulator:
|
||||
|
||||
fyne package -os ios -appID my.domain.appname
|
||||
fyne package -os iossimulator -appID my.domain.appname
|
||||
|
||||
# Preparing a release
|
||||
|
||||
Using the fyne utility "release" subcommand you can package up your app for release
|
||||
to app stores and market places. Make sure you have the standard build tools installed
|
||||
and have followed the platform documentation for setting up accounts and signing.
|
||||
Then you can execute something like the following, notice the `-os ios` parameter allows
|
||||
building an iOS app from macOS computer. Other combinations work as well :)
|
||||
|
||||
$ fyne release -os ios -certificate "Apple Distribution" -profile "My App Distribution" -appID "com.example.myapp"
|
||||
|
||||
The above command will create a '.ipa' file that can then be uploaded to the iOS App Store.
|
||||
|
||||
# Documentation
|
||||
|
||||
More documentation is available at the [Fyne developer website](https://developer.fyne.io/) or on [pkg.go.dev](https://pkg.go.dev/fyne.io/fyne/v2?tab=doc).
|
||||
|
||||
# Examples
|
||||
|
||||
You can find many example applications in the [examples repository](https://github.com/fyne-io/examples/).
|
||||
Alternatively a list of applications using fyne can be found at [our website](https://apps.fyne.io/).
|
||||
|
||||
# Shipping the Fyne Toolkit
|
||||
|
||||
All Fyne apps will work without pre-installed libraries, this is one reason the apps are so portable.
|
||||
However, if looking to support Fyne in a bigger way on your operating system then you can install some utilities that help to make a more complete experience.
|
||||
|
||||
## Additional apps
|
||||
|
||||
It is recommended that you install the following additional apps:
|
||||
|
||||
| app | go install | description |
|
||||
| ------------- | ----------------------------------- | ---------------------------------------------------------------------- |
|
||||
| fyne_settings | `fyne.io/fyne/v2/cmd/fyne_settings` | A GUI for managing your global Fyne settings like theme and scaling |
|
||||
| apps | `github.com/fyne-io/apps` | A graphical installer for the Fyne apps listed at https://apps.fyne.io |
|
||||
|
||||
These are optional applications but can help to create a more complete desktop experience.
|
||||
|
||||
## FyneDesk (Linux / BSD)
|
||||
|
||||
To go all the way with Fyne on your desktop / laptop computer you could install [FyneDesk](https://github.com/fyshos/fynedesk) as well :)
|
||||
|
||||

|
15
vendor/fyne.io/fyne/v2/SECURITY.md
generated
vendored
Normal file
15
vendor/fyne.io/fyne/v2/SECURITY.md
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Minor releases will receive security updates and fixes until the next minor or major release.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.4.x | :white_check_mark: |
|
||||
| < 2.4.0 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Report security vulnerabilities using the [advisories](https://github.com/fyne-io/fyne/security/advisories) page on GitHub.
|
||||
The team of core developers will evaluate and address the issue as appropriate.
|
84
vendor/fyne.io/fyne/v2/animation.go
generated
vendored
Normal file
84
vendor/fyne.io/fyne/v2/animation.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
package fyne
|
||||
|
||||
import "time"
|
||||
|
||||
// AnimationCurve represents an animation algorithm for calculating the progress through a timeline.
|
||||
// Custom animations can be provided by implementing the "func(float32) float32" definition.
|
||||
// The input parameter will start at 0.0 when an animation starts and travel up to 1.0 at which point it will end.
|
||||
// A linear animation would return the same output value as is passed in.
|
||||
type AnimationCurve func(float32) float32
|
||||
|
||||
// AnimationRepeatForever is an AnimationCount value that indicates it should not stop looping.
|
||||
//
|
||||
// Since: 2.0
|
||||
const AnimationRepeatForever = -1
|
||||
|
||||
var (
|
||||
// AnimationEaseInOut is the default easing, it starts slowly, accelerates to the middle and slows to the end.
|
||||
//
|
||||
// Since: 2.0
|
||||
AnimationEaseInOut = animationEaseInOut
|
||||
// AnimationEaseIn starts slowly and accelerates to the end.
|
||||
//
|
||||
// Since: 2.0
|
||||
AnimationEaseIn = animationEaseIn
|
||||
// AnimationEaseOut starts at speed and slows to the end.
|
||||
//
|
||||
// Since: 2.0
|
||||
AnimationEaseOut = animationEaseOut
|
||||
// AnimationLinear is a linear mapping for animations that progress uniformly through their duration.
|
||||
//
|
||||
// Since: 2.0
|
||||
AnimationLinear = animationLinear
|
||||
)
|
||||
|
||||
// Animation represents an animated element within a Fyne canvas.
|
||||
// These animations may control individual objects or entire scenes.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Animation struct {
|
||||
AutoReverse bool
|
||||
Curve AnimationCurve
|
||||
Duration time.Duration
|
||||
RepeatCount int
|
||||
Tick func(float32)
|
||||
}
|
||||
|
||||
// NewAnimation creates a very basic animation where the callback function will be called for every
|
||||
// rendered frame between time.Now() and the specified duration. The callback values start at 0.0 and
|
||||
// will be 1.0 when the animation completes.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewAnimation(d time.Duration, fn func(float32)) *Animation {
|
||||
return &Animation{Duration: d, Tick: fn}
|
||||
}
|
||||
|
||||
// Start registers the animation with the application run-loop and starts its execution.
|
||||
func (a *Animation) Start() {
|
||||
CurrentApp().Driver().StartAnimation(a)
|
||||
}
|
||||
|
||||
// Stop will end this animation and remove it from the run-loop.
|
||||
func (a *Animation) Stop() {
|
||||
CurrentApp().Driver().StopAnimation(a)
|
||||
}
|
||||
|
||||
func animationEaseIn(val float32) float32 {
|
||||
return val * val
|
||||
}
|
||||
|
||||
func animationEaseInOut(val float32) float32 {
|
||||
if val <= 0.5 {
|
||||
return val * val * 2
|
||||
}
|
||||
|
||||
return -1 + (4-val*2)*val
|
||||
}
|
||||
|
||||
func animationEaseOut(val float32) float32 {
|
||||
return val * (2 - val)
|
||||
}
|
||||
|
||||
func animationLinear(val float32) float32 {
|
||||
return val
|
||||
}
|
144
vendor/fyne.io/fyne/v2/app.go
generated
vendored
Normal file
144
vendor/fyne.io/fyne/v2/app.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
package fyne
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// An App is the definition of a graphical application.
|
||||
// Apps can have multiple windows, by default they will exit when all windows
|
||||
// have been closed. This can be modified using SetMaster() or SetCloseIntercept().
|
||||
// To start an application you need to call Run() somewhere in your main() function.
|
||||
// Alternatively use the window.ShowAndRun() function for your main window.
|
||||
type App interface {
|
||||
// Create a new window for the application.
|
||||
// The first window to open is considered the "master" and when closed
|
||||
// the application will exit.
|
||||
NewWindow(title string) Window
|
||||
|
||||
// Open a URL in the default browser application.
|
||||
OpenURL(url *url.URL) error
|
||||
|
||||
// Icon returns the application icon, this is used in various ways
|
||||
// depending on operating system.
|
||||
// This is also the default icon for new windows.
|
||||
Icon() Resource
|
||||
|
||||
// SetIcon sets the icon resource used for this application instance.
|
||||
SetIcon(Resource)
|
||||
|
||||
// Run the application - this starts the event loop and waits until Quit()
|
||||
// is called or the last window closes.
|
||||
// This should be called near the end of a main() function as it will block.
|
||||
Run()
|
||||
|
||||
// Calling Quit on the application will cause the application to exit
|
||||
// cleanly, closing all open windows.
|
||||
// This function does no thing on a mobile device as the application lifecycle is
|
||||
// managed by the operating system.
|
||||
Quit()
|
||||
|
||||
// Driver returns the driver that is rendering this application.
|
||||
// Typically not needed for day to day work, mostly internal functionality.
|
||||
Driver() Driver
|
||||
|
||||
// UniqueID returns the application unique identifier, if set.
|
||||
// This must be set for use of the Preferences() functions... see NewWithId(string)
|
||||
UniqueID() string
|
||||
|
||||
// SendNotification sends a system notification that will be displayed in the operating system's notification area.
|
||||
SendNotification(*Notification)
|
||||
|
||||
// Settings return the globally set settings, determining theme and so on.
|
||||
Settings() Settings
|
||||
|
||||
// Preferences returns the application preferences, used for storing configuration and state
|
||||
Preferences() Preferences
|
||||
|
||||
// Storage returns a storage handler specific to this application.
|
||||
Storage() Storage
|
||||
|
||||
// Lifecycle returns a type that allows apps to hook in to lifecycle events.
|
||||
//
|
||||
// Since: 2.1
|
||||
Lifecycle() Lifecycle
|
||||
|
||||
// Metadata returns the application metadata that was set at compile time.
|
||||
//
|
||||
// Since: 2.2
|
||||
Metadata() AppMetadata
|
||||
|
||||
// CloudProvider returns the current app cloud provider,
|
||||
// if one has been registered by the developer or chosen by the user.
|
||||
//
|
||||
// Since: 2.3
|
||||
CloudProvider() CloudProvider // get the (if any) configured provider
|
||||
|
||||
// SetCloudProvider allows developers to specify how this application should integrate with cloud services.
|
||||
// See `fyne.io/cloud` package for implementation details.
|
||||
//
|
||||
// Since: 2.3
|
||||
SetCloudProvider(CloudProvider) // configure cloud for this app
|
||||
}
|
||||
|
||||
// app contains an App variable, but due to atomic.Value restrictions on
|
||||
// interfaces we need to use an indirect type, i.e. appContainer.
|
||||
var app atomic.Value // appContainer
|
||||
|
||||
// appContainer is a dummy container that holds an App instance. This
|
||||
// struct exists to guarantee that atomic.Value can store objects with
|
||||
// same type.
|
||||
type appContainer struct {
|
||||
current App
|
||||
}
|
||||
|
||||
// SetCurrentApp is an internal function to set the app instance currently running.
|
||||
func SetCurrentApp(current App) {
|
||||
app.Store(appContainer{current})
|
||||
}
|
||||
|
||||
// CurrentApp returns the current application, for which there is only 1 per process.
|
||||
func CurrentApp() App {
|
||||
val := app.Load()
|
||||
if val == nil {
|
||||
LogError("Attempt to access current Fyne app when none is started", nil)
|
||||
return nil
|
||||
}
|
||||
return (val).(appContainer).current
|
||||
}
|
||||
|
||||
// AppMetadata captures the build metadata for an application.
|
||||
//
|
||||
// Since: 2.2
|
||||
type AppMetadata struct {
|
||||
// ID is the unique ID of this application, used by many distribution platforms.
|
||||
ID string
|
||||
// Name is the human friendly name of this app.
|
||||
Name string
|
||||
// Version represents the version of this application, normally following semantic versioning.
|
||||
Version string
|
||||
// Build is the build number of this app, some times appended to the version number.
|
||||
Build int
|
||||
// Icon contains, if present, a resource of the icon that was bundled at build time.
|
||||
Icon Resource
|
||||
// Release if true this binary was build in release mode
|
||||
// Since 2.3
|
||||
Release bool
|
||||
// Custom contain the custom metadata defined either in FyneApp.toml or on the compile command line
|
||||
// Since 2.3
|
||||
Custom map[string]string
|
||||
}
|
||||
|
||||
// Lifecycle represents the various phases that an app can transition through.
|
||||
//
|
||||
// Since: 2.1
|
||||
type Lifecycle interface {
|
||||
// SetOnEnteredForeground hooks into the app becoming foreground and gaining focus.
|
||||
SetOnEnteredForeground(func())
|
||||
// SetOnExitedForeground hooks into the app losing input focus and going into the background.
|
||||
SetOnExitedForeground(func())
|
||||
// SetOnStarted hooks into an event that says the app is now running.
|
||||
SetOnStarted(func())
|
||||
// SetOnStopped hooks into an event that says the app is no longer running.
|
||||
SetOnStopped(func())
|
||||
}
|
169
vendor/fyne.io/fyne/v2/app/app.go
generated
vendored
Normal file
169
vendor/fyne.io/fyne/v2/app/app.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
// Package app provides app implementations for working with Fyne graphical interfaces.
|
||||
// The fastest way to get started is to call app.New() which will normally load a new desktop application.
|
||||
// If the "ci" tag is passed to go (go run -tags ci myapp.go) it will run an in-memory application.
|
||||
package app // import "fyne.io/fyne/v2/app"
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal"
|
||||
"fyne.io/fyne/v2/internal/app"
|
||||
intRepo "fyne.io/fyne/v2/internal/repository"
|
||||
"fyne.io/fyne/v2/storage/repository"
|
||||
)
|
||||
|
||||
// Declare conformity with App interface
|
||||
var _ fyne.App = (*fyneApp)(nil)
|
||||
|
||||
type fyneApp struct {
|
||||
driver fyne.Driver
|
||||
icon fyne.Resource
|
||||
uniqueID string
|
||||
|
||||
cloud fyne.CloudProvider
|
||||
lifecycle fyne.Lifecycle
|
||||
settings *settings
|
||||
storage fyne.Storage
|
||||
prefs fyne.Preferences
|
||||
|
||||
running uint32 // atomic, 1 == running, 0 == stopped
|
||||
}
|
||||
|
||||
func (a *fyneApp) CloudProvider() fyne.CloudProvider {
|
||||
return a.cloud
|
||||
}
|
||||
|
||||
func (a *fyneApp) Icon() fyne.Resource {
|
||||
if a.icon != nil {
|
||||
return a.icon
|
||||
}
|
||||
|
||||
return a.Metadata().Icon
|
||||
}
|
||||
|
||||
func (a *fyneApp) SetIcon(icon fyne.Resource) {
|
||||
a.icon = icon
|
||||
}
|
||||
|
||||
func (a *fyneApp) UniqueID() string {
|
||||
if a.uniqueID != "" {
|
||||
return a.uniqueID
|
||||
}
|
||||
if a.Metadata().ID != "" {
|
||||
return a.Metadata().ID
|
||||
}
|
||||
|
||||
fyne.LogError("Preferences API requires a unique ID, use app.NewWithID() or the FyneApp.toml ID field", nil)
|
||||
a.uniqueID = "missing-id-" + strconv.FormatInt(time.Now().Unix(), 10) // This is a fake unique - it just has to not be reused...
|
||||
return a.uniqueID
|
||||
}
|
||||
|
||||
func (a *fyneApp) NewWindow(title string) fyne.Window {
|
||||
return a.driver.CreateWindow(title)
|
||||
}
|
||||
|
||||
func (a *fyneApp) Run() {
|
||||
if atomic.CompareAndSwapUint32(&a.running, 0, 1) {
|
||||
a.driver.Run()
|
||||
}
|
||||
}
|
||||
|
||||
func (a *fyneApp) Quit() {
|
||||
for _, window := range a.driver.AllWindows() {
|
||||
window.Close()
|
||||
}
|
||||
|
||||
a.driver.Quit()
|
||||
a.settings.stopWatching()
|
||||
atomic.StoreUint32(&a.running, 0)
|
||||
}
|
||||
|
||||
func (a *fyneApp) Driver() fyne.Driver {
|
||||
return a.driver
|
||||
}
|
||||
|
||||
// Settings returns the application settings currently configured.
|
||||
func (a *fyneApp) Settings() fyne.Settings {
|
||||
return a.settings
|
||||
}
|
||||
|
||||
func (a *fyneApp) Storage() fyne.Storage {
|
||||
return a.storage
|
||||
}
|
||||
|
||||
func (a *fyneApp) Preferences() fyne.Preferences {
|
||||
if a.UniqueID() == "" {
|
||||
fyne.LogError("Preferences API requires a unique ID, use app.NewWithID() or the FyneApp.toml ID field", nil)
|
||||
}
|
||||
return a.prefs
|
||||
}
|
||||
|
||||
func (a *fyneApp) Lifecycle() fyne.Lifecycle {
|
||||
return a.lifecycle
|
||||
}
|
||||
|
||||
func (a *fyneApp) newDefaultPreferences() *preferences {
|
||||
p := newPreferences(a)
|
||||
if a.uniqueID != "" {
|
||||
p.load()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// New returns a new application instance with the default driver and no unique ID (unless specified in FyneApp.toml)
|
||||
func New() fyne.App {
|
||||
if meta.ID == "" {
|
||||
internal.LogHint("Applications should be created with a unique ID using app.NewWithID()")
|
||||
}
|
||||
return NewWithID(meta.ID)
|
||||
}
|
||||
|
||||
func makeStoreDocs(id string, s *store) *internal.Docs {
|
||||
if id != "" {
|
||||
err := os.MkdirAll(s.a.storageRoot(), 0755) // make the space before anyone can use it
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to create app storage space", err)
|
||||
}
|
||||
|
||||
root, _ := s.docRootURI()
|
||||
return &internal.Docs{RootDocURI: root}
|
||||
} else {
|
||||
return &internal.Docs{} // an empty impl to avoid crashes
|
||||
}
|
||||
}
|
||||
|
||||
func newAppWithDriver(d fyne.Driver, id string) fyne.App {
|
||||
newApp := &fyneApp{uniqueID: id, driver: d, lifecycle: &app.Lifecycle{}}
|
||||
fyne.SetCurrentApp(newApp)
|
||||
|
||||
newApp.prefs = newApp.newDefaultPreferences()
|
||||
newApp.lifecycle.(*app.Lifecycle).SetOnStoppedHookExecuted(func() {
|
||||
if prefs, ok := newApp.prefs.(*preferences); ok {
|
||||
prefs.forceImmediateSave()
|
||||
}
|
||||
})
|
||||
newApp.settings = loadSettings()
|
||||
store := &store{a: newApp}
|
||||
store.Docs = makeStoreDocs(id, store)
|
||||
newApp.storage = store
|
||||
|
||||
if !d.Device().IsMobile() {
|
||||
newApp.settings.watchSettings()
|
||||
}
|
||||
|
||||
httpHandler := intRepo.NewHTTPRepository()
|
||||
repository.Register("http", httpHandler)
|
||||
repository.Register("https", httpHandler)
|
||||
|
||||
return newApp
|
||||
}
|
||||
|
||||
// marker interface to pass system tray to supporting drivers
|
||||
type systrayDriver interface {
|
||||
SetSystemTrayMenu(*fyne.Menu)
|
||||
SetSystemTrayIcon(resource fyne.Resource)
|
||||
}
|
60
vendor/fyne.io/fyne/v2/app/app_darwin.go
generated
vendored
Normal file
60
vendor/fyne.io/fyne/v2/app/app_darwin.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
//go:build !ci && !js && !wasm && !test_web_driver
|
||||
// +build !ci,!js,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
bool isBundled();
|
||||
void sendNotification(char *title, char *content);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"golang.org/x/sys/execabs"
|
||||
)
|
||||
|
||||
func (a *fyneApp) SendNotification(n *fyne.Notification) {
|
||||
if C.isBundled() {
|
||||
titleStr := C.CString(n.Title)
|
||||
defer C.free(unsafe.Pointer(titleStr))
|
||||
contentStr := C.CString(n.Content)
|
||||
defer C.free(unsafe.Pointer(contentStr))
|
||||
|
||||
C.sendNotification(titleStr, contentStr)
|
||||
return
|
||||
}
|
||||
|
||||
fallbackNotification(n.Title, n.Content)
|
||||
}
|
||||
|
||||
func escapeNotificationString(in string) string {
|
||||
noSlash := strings.ReplaceAll(in, "\\", "\\\\")
|
||||
return strings.ReplaceAll(noSlash, "\"", "\\\"")
|
||||
}
|
||||
|
||||
//export fallbackSend
|
||||
func fallbackSend(cTitle, cContent *C.char) {
|
||||
title := C.GoString(cTitle)
|
||||
content := C.GoString(cContent)
|
||||
fallbackNotification(title, content)
|
||||
}
|
||||
|
||||
func fallbackNotification(title, content string) {
|
||||
template := `display notification "%s" with title "%s"`
|
||||
script := fmt.Sprintf(template, escapeNotificationString(content), escapeNotificationString(title))
|
||||
|
||||
err := execabs.Command("osascript", "-e", script).Start()
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to launch darwin notify script", err)
|
||||
}
|
||||
}
|
61
vendor/fyne.io/fyne/v2/app/app_darwin.m
generated
vendored
Normal file
61
vendor/fyne.io/fyne/v2/app/app_darwin.m
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
//go:build !ci
|
||||
// +build !ci
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
|
||||
#import <UserNotifications/UserNotifications.h>
|
||||
#endif
|
||||
|
||||
static int notifyNum = 0;
|
||||
|
||||
extern void fallbackSend(char *cTitle, char *cBody);
|
||||
|
||||
bool isBundled() {
|
||||
return [[NSBundle mainBundle] bundleIdentifier] != nil;
|
||||
}
|
||||
|
||||
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101400
|
||||
void doSendNotification(UNUserNotificationCenter *center, NSString *title, NSString *body) {
|
||||
UNMutableNotificationContent *content = [UNMutableNotificationContent new];
|
||||
[content autorelease];
|
||||
content.title = title;
|
||||
content.body = body;
|
||||
|
||||
notifyNum++;
|
||||
NSString *identifier = [NSString stringWithFormat:@"fyne-notify-%d", notifyNum];
|
||||
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier
|
||||
content:content trigger:nil];
|
||||
|
||||
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (error != nil) {
|
||||
NSLog(@"Could not send notification: %@", error);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
void sendNotification(char *cTitle, char *cBody) {
|
||||
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
NSString *title = [NSString stringWithUTF8String:cTitle];
|
||||
NSString *body = [NSString stringWithUTF8String:cBody];
|
||||
|
||||
UNAuthorizationOptions options = UNAuthorizationOptionAlert;
|
||||
[center requestAuthorizationWithOptions:options
|
||||
completionHandler:^(BOOL granted, NSError *_Nullable error) {
|
||||
if (!granted) {
|
||||
if (error != NULL) {
|
||||
NSLog(@"Error asking for permission to send notifications %@", error);
|
||||
// this happens if our app was not signed, so do it the old way
|
||||
fallbackSend((char *)[title UTF8String], (char *)[body UTF8String]);
|
||||
} else {
|
||||
NSLog(@"Unable to get permission to send notifications");
|
||||
}
|
||||
} else {
|
||||
doSendNotification(center, title, body);
|
||||
}
|
||||
}];
|
||||
}
|
||||
#else
|
||||
void sendNotification(char *cTitle, char *cBody) {
|
||||
fallbackSend(cTitle, cBody);
|
||||
}
|
||||
#endif
|
8
vendor/fyne.io/fyne/v2/app/app_debug.go
generated
vendored
Normal file
8
vendor/fyne.io/fyne/v2/app/app_debug.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build debug
|
||||
// +build debug
|
||||
|
||||
package app
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
const buildMode = fyne.BuildDebug
|
69
vendor/fyne.io/fyne/v2/app/app_desktop_darwin.go
generated
vendored
Normal file
69
vendor/fyne.io/fyne/v2/app/app_desktop_darwin.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
//go:build !ci && !ios && !js && !wasm && !test_web_driver
|
||||
// +build !ci,!ios,!js,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation
|
||||
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
bool isBundled();
|
||||
bool isDarkMode();
|
||||
void watchTheme();
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/execabs"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
// SetSystemTrayMenu creates a system tray item and attaches the specified menu.
|
||||
// By default this will use the application icon.
|
||||
func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) {
|
||||
if desk, ok := a.Driver().(systrayDriver); ok {
|
||||
desk.SetSystemTrayMenu(menu)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSystemTrayIcon sets a custom image for the system tray icon.
|
||||
// You should have previously called `SetSystemTrayMenu` to initialise the menu icon.
|
||||
func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) {
|
||||
a.Driver().(systrayDriver).SetSystemTrayIcon(icon)
|
||||
}
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
if C.isDarkMode() {
|
||||
return theme.VariantDark
|
||||
}
|
||||
return theme.VariantLight
|
||||
}
|
||||
|
||||
func rootConfigDir() string {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
|
||||
desktopConfig := filepath.Join(filepath.Join(homeDir, "Library"), "Preferences")
|
||||
return filepath.Join(desktopConfig, "fyne")
|
||||
}
|
||||
|
||||
func (a *fyneApp) OpenURL(url *url.URL) error {
|
||||
cmd := execabs.Command("open", url.String())
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
//export themeChanged
|
||||
func themeChanged() {
|
||||
fyne.CurrentApp().Settings().(*settings).setupTheme()
|
||||
}
|
||||
|
||||
func watchTheme() {
|
||||
C.watchTheme()
|
||||
}
|
18
vendor/fyne.io/fyne/v2/app/app_desktop_darwin.m
generated
vendored
Normal file
18
vendor/fyne.io/fyne/v2/app/app_desktop_darwin.m
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
//go:build !ci && !ios
|
||||
// +build !ci,!ios
|
||||
|
||||
extern void themeChanged();
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
bool isDarkMode() {
|
||||
NSString *style = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"];
|
||||
return [@"Dark" isEqualToString:style];
|
||||
}
|
||||
|
||||
void watchTheme() {
|
||||
[[NSDistributedNotificationCenter defaultCenter] addObserverForName:@"AppleInterfaceThemeChangedNotification" object:nil queue:nil
|
||||
usingBlock:^(NSNotification *note) {
|
||||
themeChanged(); // calls back into Go
|
||||
}];
|
||||
}
|
15
vendor/fyne.io/fyne/v2/app/app_gl.go
generated
vendored
Normal file
15
vendor/fyne.io/fyne/v2/app/app_gl.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
//go:build !ci && !android && !ios && !mobile
|
||||
// +build !ci,!android,!ios,!mobile
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal/driver/glfw"
|
||||
)
|
||||
|
||||
// NewWithID returns a new app instance using the appropriate runtime driver.
|
||||
// The ID string should be globally unique to this app.
|
||||
func NewWithID(id string) fyne.App {
|
||||
return newAppWithDriver(glfw.NewGLDriver(), id)
|
||||
}
|
19
vendor/fyne.io/fyne/v2/app/app_goxjs.go
generated
vendored
Normal file
19
vendor/fyne.io/fyne/v2/app/app_goxjs.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build !ci && (!android || !ios || !mobile) && (js || wasm || test_web_driver)
|
||||
// +build !ci
|
||||
// +build !android !ios !mobile
|
||||
// +build js wasm test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
func (app *fyneApp) SendNotification(_ *fyne.Notification) {
|
||||
// TODO #2735
|
||||
fyne.LogError("Sending notification is not supported yet.", nil)
|
||||
}
|
||||
|
||||
func rootConfigDir() string {
|
||||
return "/data/"
|
||||
}
|
25
vendor/fyne.io/fyne/v2/app/app_mobile.go
generated
vendored
Normal file
25
vendor/fyne.io/fyne/v2/app/app_mobile.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
//go:build !ci && (android || ios || mobile)
|
||||
// +build !ci
|
||||
// +build android ios mobile
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal/driver/mobile"
|
||||
)
|
||||
|
||||
var systemTheme fyne.ThemeVariant
|
||||
|
||||
// NewWithID returns a new app instance using the appropriate runtime driver.
|
||||
// The ID string should be globally unique to this app.
|
||||
func NewWithID(id string) fyne.App {
|
||||
d := mobile.NewGoMobileDriver()
|
||||
a := newAppWithDriver(d, id)
|
||||
d.(mobile.ConfiguredDriver).SetOnConfigurationChanged(func(c *mobile.Configuration) {
|
||||
systemTheme = c.SystemTheme
|
||||
|
||||
a.Settings().(*settings).setupTheme()
|
||||
})
|
||||
return a
|
||||
}
|
131
vendor/fyne.io/fyne/v2/app/app_mobile_and.c
generated
vendored
Normal file
131
vendor/fyne.io/fyne/v2/app/app_mobile_and.c
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
//go:build !ci && android
|
||||
// +build !ci,android
|
||||
|
||||
#include <android/log.h>
|
||||
#include <jni.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Fyne", __VA_ARGS__)
|
||||
|
||||
static jclass find_class(JNIEnv *env, const char *class_name) {
|
||||
jclass clazz = (*env)->FindClass(env, class_name);
|
||||
if (clazz == NULL) {
|
||||
(*env)->ExceptionClear(env);
|
||||
LOG_FATAL("cannot find %s", class_name);
|
||||
return NULL;
|
||||
}
|
||||
return clazz;
|
||||
}
|
||||
|
||||
static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
|
||||
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
|
||||
if (m == 0) {
|
||||
(*env)->ExceptionClear(env);
|
||||
LOG_FATAL("cannot find method %s %s", name, sig);
|
||||
return 0;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static jmethodID find_static_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
|
||||
jmethodID m = (*env)->GetStaticMethodID(env, clazz, name, sig);
|
||||
if (m == 0) {
|
||||
(*env)->ExceptionClear(env);
|
||||
LOG_FATAL("cannot find method %s %s", name, sig);
|
||||
return 0;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
jobject getSystemService(uintptr_t jni_env, uintptr_t ctx, char *service) {
|
||||
JNIEnv *env = (JNIEnv*)jni_env;
|
||||
jstring serviceStr = (*env)->NewStringUTF(env, service);
|
||||
|
||||
jclass ctxClass = (*env)->GetObjectClass(env, (jobject)ctx);
|
||||
jmethodID getSystemService = find_method(env, ctxClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
||||
|
||||
return (jobject)(*env)->CallObjectMethod(env, (jobject)ctx, getSystemService, serviceStr);
|
||||
}
|
||||
|
||||
int nextId = 1;
|
||||
|
||||
bool isOreoOrLater(JNIEnv *env) {
|
||||
jclass versionClass = find_class(env, "android/os/Build$VERSION" );
|
||||
jfieldID sdkIntFieldID = (*env)->GetStaticFieldID(env, versionClass, "SDK_INT", "I" );
|
||||
int sdkVersion = (*env)->GetStaticIntField(env, versionClass, sdkIntFieldID );
|
||||
|
||||
return sdkVersion >= 26; // O = Oreo, will not be defined for older builds
|
||||
}
|
||||
|
||||
jobject parseURL(uintptr_t jni_env, uintptr_t ctx, char* uriCstr) {
|
||||
JNIEnv *env = (JNIEnv*)jni_env;
|
||||
|
||||
jstring uriStr = (*env)->NewStringUTF(env, uriCstr);
|
||||
jclass uriClass = find_class(env, "android/net/Uri");
|
||||
jmethodID parse = find_static_method(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;");
|
||||
|
||||
return (jobject)(*env)->CallStaticObjectMethod(env, uriClass, parse, uriStr);
|
||||
}
|
||||
|
||||
void openURL(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char *url) {
|
||||
JNIEnv *env = (JNIEnv*)jni_env;
|
||||
jobject uri = parseURL(jni_env, ctx, url);
|
||||
|
||||
jclass intentClass = find_class(env, "android/content/Intent");
|
||||
jfieldID viewFieldID = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;" );
|
||||
jstring view = (*env)->GetStaticObjectField(env, intentClass, viewFieldID);
|
||||
|
||||
jmethodID constructor = find_method(env, intentClass, "<init>", "(Ljava/lang/String;Landroid/net/Uri;)V");
|
||||
jobject intent = (*env)->NewObject(env, intentClass, constructor, view, uri);
|
||||
|
||||
jclass contextClass = find_class(env, "android/content/Context");
|
||||
jmethodID start = find_method(env, contextClass, "startActivity", "(Landroid/content/Intent;)V");
|
||||
(*env)->CallVoidMethod(env, (jobject)ctx, start, intent);
|
||||
}
|
||||
|
||||
void sendNotification(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char *title, char *body) {
|
||||
JNIEnv *env = (JNIEnv*)jni_env;
|
||||
jstring titleStr = (*env)->NewStringUTF(env, title);
|
||||
jstring bodyStr = (*env)->NewStringUTF(env, body);
|
||||
|
||||
jclass cls = find_class(env, "android/app/Notification$Builder");
|
||||
jmethodID constructor = find_method(env, cls, "<init>", "(Landroid/content/Context;)V");
|
||||
jobject builder = (*env)->NewObject(env, cls, constructor, ctx);
|
||||
|
||||
jclass mgrCls = find_class(env, "android/app/NotificationManager");
|
||||
jobject mgr = getSystemService((uintptr_t)env, ctx, "notification");
|
||||
|
||||
if (isOreoOrLater(env)) {
|
||||
jstring channelId = (*env)->NewStringUTF(env, "fyne-notif");
|
||||
jstring name = (*env)->NewStringUTF(env, "Fyne Notification");
|
||||
int importance = 4; // IMPORTANCE_HIGH
|
||||
|
||||
jclass chanCls = find_class(env, "android/app/NotificationChannel");
|
||||
jmethodID constructor = find_method(env, chanCls, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V");
|
||||
jobject channel = (*env)->NewObject(env, chanCls, constructor, channelId, name, importance);
|
||||
|
||||
jmethodID createChannel = find_method(env, mgrCls, "createNotificationChannel", "(Landroid/app/NotificationChannel;)V");
|
||||
(*env)->CallVoidMethod(env, mgr, createChannel, channel);
|
||||
|
||||
jmethodID setChannelId = find_method(env, cls, "setChannelId", "(Ljava/lang/String;)Landroid/app/Notification$Builder;");
|
||||
(*env)->CallObjectMethod(env, builder, setChannelId, channelId);
|
||||
}
|
||||
|
||||
jmethodID setContentTitle = find_method(env, cls, "setContentTitle", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;");
|
||||
(*env)->CallObjectMethod(env, builder, setContentTitle, titleStr);
|
||||
|
||||
jmethodID setContentText = find_method(env, cls, "setContentText", "(Ljava/lang/CharSequence;)Landroid/app/Notification$Builder;");
|
||||
(*env)->CallObjectMethod(env, builder, setContentText, bodyStr);
|
||||
|
||||
int iconID = 17629184; // constant of "unknown app icon"
|
||||
jmethodID setSmallIcon = find_method(env, cls, "setSmallIcon", "(I)Landroid/app/Notification$Builder;");
|
||||
(*env)->CallObjectMethod(env, builder, setSmallIcon, iconID);
|
||||
|
||||
jmethodID build = find_method(env, cls, "build", "()Landroid/app/Notification;");
|
||||
jobject notif = (*env)->CallObjectMethod(env, builder, build);
|
||||
|
||||
jmethodID notify = find_method(env, mgrCls, "notify", "(ILandroid/app/Notification;)V");
|
||||
(*env)->CallVoidMethod(env, mgr, notify, nextId, notif);
|
||||
nextId++;
|
||||
}
|
61
vendor/fyne.io/fyne/v2/app/app_mobile_and.go
generated
vendored
Normal file
61
vendor/fyne.io/fyne/v2/app/app_mobile_and.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
//go:build !ci && android
|
||||
// +build !ci,android
|
||||
|
||||
package app
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -landroid -llog
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
void openURL(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char *url);
|
||||
void sendNotification(uintptr_t java_vm, uintptr_t jni_env, uintptr_t ctx, char *title, char *content);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal/driver/mobile/app"
|
||||
)
|
||||
|
||||
func (a *fyneApp) OpenURL(url *url.URL) error {
|
||||
urlStr := C.CString(url.String())
|
||||
defer C.free(unsafe.Pointer(urlStr))
|
||||
|
||||
app.RunOnJVM(func(vm, env, ctx uintptr) error {
|
||||
C.openURL(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx), urlStr)
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *fyneApp) SendNotification(n *fyne.Notification) {
|
||||
titleStr := C.CString(n.Title)
|
||||
defer C.free(unsafe.Pointer(titleStr))
|
||||
contentStr := C.CString(n.Content)
|
||||
defer C.free(unsafe.Pointer(contentStr))
|
||||
|
||||
app.RunOnJVM(func(vm, env, ctx uintptr) error {
|
||||
C.sendNotification(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx), titleStr, contentStr)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
return systemTheme
|
||||
}
|
||||
|
||||
func rootConfigDir() string {
|
||||
filesDir := os.Getenv("FILESDIR")
|
||||
if filesDir == "" {
|
||||
log.Println("FILESDIR env was not set by android native code")
|
||||
return "/data/data" // probably won't work, but we can't make a better guess
|
||||
}
|
||||
|
||||
return filepath.Join(filesDir, "fyne")
|
||||
}
|
40
vendor/fyne.io/fyne/v2/app/app_mobile_ios.go
generated
vendored
Normal file
40
vendor/fyne.io/fyne/v2/app/app_mobile_ios.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build !ci && ios
|
||||
// +build !ci,ios
|
||||
|
||||
package app
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -x objective-c
|
||||
#cgo LDFLAGS: -framework Foundation -framework UIKit -framework UserNotifications
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
char *documentsPath(void);
|
||||
void openURL(char *urlStr);
|
||||
void sendNotification(char *title, char *content);
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"unsafe"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
func rootConfigDir() string {
|
||||
root := C.documentsPath()
|
||||
return filepath.Join(C.GoString(root), "fyne")
|
||||
}
|
||||
|
||||
func (a *fyneApp) OpenURL(url *url.URL) error {
|
||||
urlStr := C.CString(url.String())
|
||||
C.openURL(urlStr)
|
||||
C.free(unsafe.Pointer(urlStr))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
return systemTheme
|
||||
}
|
16
vendor/fyne.io/fyne/v2/app/app_mobile_ios.m
generated
vendored
Normal file
16
vendor/fyne.io/fyne/v2/app/app_mobile_ios.m
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build !ci && ios
|
||||
// +build !ci,ios
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
void openURL(char *urlStr) {
|
||||
UIApplication *app = [UIApplication sharedApplication];
|
||||
NSURL *url = [NSURL URLWithString:[NSString stringWithUTF8String:urlStr]];
|
||||
[app openURL:url options:@{} completionHandler:nil];
|
||||
}
|
||||
|
||||
char *documentsPath() {
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
||||
NSString *path = paths.firstObject;
|
||||
return [path UTF8String];
|
||||
}
|
9
vendor/fyne.io/fyne/v2/app/app_notlegacy_darwin.go
generated
vendored
Normal file
9
vendor/fyne.io/fyne/v2/app/app_notlegacy_darwin.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
//go:build !ci && !legacy && !js && !wasm && !test_web_driver
|
||||
// +build !ci,!legacy,!js,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -framework Foundation -framework UserNotifications
|
||||
*/
|
||||
import "C"
|
20
vendor/fyne.io/fyne/v2/app/app_openurl_js.go
generated
vendored
Normal file
20
vendor/fyne.io/fyne/v2/app/app_openurl_js.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
//go:build !ci && js && !wasm
|
||||
// +build !ci,js,!wasm
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"honnef.co/go/js/dom"
|
||||
)
|
||||
|
||||
func (app *fyneApp) OpenURL(url *url.URL) error {
|
||||
window := dom.GetWindow().Open(url.String(), "_blank", "")
|
||||
if window == nil {
|
||||
return fmt.Errorf("Unable to open a new window/tab for URL: %v.", url)
|
||||
}
|
||||
window.Focus()
|
||||
return nil
|
||||
}
|
19
vendor/fyne.io/fyne/v2/app/app_openurl_wasm.go
generated
vendored
Normal file
19
vendor/fyne.io/fyne/v2/app/app_openurl_wasm.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build !ci && wasm
|
||||
// +build !ci,wasm
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
func (app *fyneApp) OpenURL(url *url.URL) error {
|
||||
window := js.Global().Call("open", url.String(), "_blank", "")
|
||||
if window.Equal(js.Null()) {
|
||||
return fmt.Errorf("Unable to open a new window/tab for URL: %v.", url)
|
||||
}
|
||||
window.Call("focus")
|
||||
return nil
|
||||
}
|
13
vendor/fyne.io/fyne/v2/app/app_openurl_web.go
generated
vendored
Normal file
13
vendor/fyne.io/fyne/v2/app/app_openurl_web.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build !ci && !js && !wasm && test_web_driver
|
||||
// +build !ci,!js,!wasm,test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
func (app *fyneApp) OpenURL(url *url.URL) error {
|
||||
return errors.New("OpenURL is not supported with the test web driver.")
|
||||
}
|
34
vendor/fyne.io/fyne/v2/app/app_other.go
generated
vendored
Normal file
34
vendor/fyne.io/fyne/v2/app/app_other.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
//go:build ci || (!linux && !darwin && !windows && !freebsd && !openbsd && !netbsd && !js && !wasm && !test_web_driver)
|
||||
// +build ci !linux,!darwin,!windows,!freebsd,!openbsd,!netbsd,!js,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
return theme.VariantDark
|
||||
}
|
||||
|
||||
func rootConfigDir() string {
|
||||
return filepath.Join(os.TempDir(), "fyne-test")
|
||||
}
|
||||
|
||||
func (a *fyneApp) OpenURL(_ *url.URL) error {
|
||||
return errors.New("Unable to open url for unknown operating system")
|
||||
}
|
||||
|
||||
func (a *fyneApp) SendNotification(_ *fyne.Notification) {
|
||||
fyne.LogError("Refusing to show notification for unknown operating system", nil)
|
||||
}
|
||||
|
||||
func watchTheme() {
|
||||
// no-op
|
||||
}
|
8
vendor/fyne.io/fyne/v2/app/app_release.go
generated
vendored
Normal file
8
vendor/fyne.io/fyne/v2/app/app_release.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build release
|
||||
// +build release
|
||||
|
||||
package app
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
const buildMode = fyne.BuildRelease
|
16
vendor/fyne.io/fyne/v2/app/app_software.go
generated
vendored
Normal file
16
vendor/fyne.io/fyne/v2/app/app_software.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build ci
|
||||
// +build ci
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal/painter/software"
|
||||
"fyne.io/fyne/v2/test"
|
||||
)
|
||||
|
||||
// NewWithID returns a new app instance using the test (headless) driver.
|
||||
// The ID string should be globally unique to this app.
|
||||
func NewWithID(id string) fyne.App {
|
||||
return newAppWithDriver(test.NewDriverWithPainter(software.NewPainter()), id)
|
||||
}
|
8
vendor/fyne.io/fyne/v2/app/app_standard.go
generated
vendored
Normal file
8
vendor/fyne.io/fyne/v2/app/app_standard.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build !debug && !release
|
||||
// +build !debug,!release
|
||||
|
||||
package app
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
const buildMode = fyne.BuildStandard
|
29
vendor/fyne.io/fyne/v2/app/app_theme_js.go
generated
vendored
Normal file
29
vendor/fyne.io/fyne/v2/app/app_theme_js.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
//go:build !ci && js && !wasm
|
||||
// +build !ci,js,!wasm
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
|
||||
"github.com/gopherjs/gopherjs/js"
|
||||
)
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
if matchMedia := js.Global.Call("matchMedia", "(prefers-color-scheme: dark)"); matchMedia != js.Undefined {
|
||||
if matches := matchMedia.Get("matches"); matches != js.Undefined && matches.Bool() {
|
||||
return theme.VariantDark
|
||||
}
|
||||
return theme.VariantLight
|
||||
}
|
||||
return theme.VariantDark
|
||||
}
|
||||
|
||||
func init() {
|
||||
if matchMedia := js.Global.Call("matchMedia", "(prefers-color-scheme: dark)"); matchMedia != js.Undefined {
|
||||
matchMedia.Call("addEventListener", "change", func(o *js.Object) {
|
||||
fyne.CurrentApp().Settings().(*settings).setupTheme()
|
||||
})
|
||||
}
|
||||
}
|
31
vendor/fyne.io/fyne/v2/app/app_theme_wasm.go
generated
vendored
Normal file
31
vendor/fyne.io/fyne/v2/app/app_theme_wasm.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
//go:build !ci && wasm
|
||||
// +build !ci,wasm
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"syscall/js"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
matches := js.Global().Call("matchMedia", "(prefers-color-scheme: dark)")
|
||||
if matches.Truthy() {
|
||||
if matches.Get("matches").Bool() {
|
||||
return theme.VariantDark
|
||||
}
|
||||
return theme.VariantLight
|
||||
}
|
||||
return theme.VariantDark
|
||||
}
|
||||
|
||||
func init() {
|
||||
if matchMedia := js.Global().Call("matchMedia", "(prefers-color-scheme: dark)"); matchMedia.Truthy() {
|
||||
matchMedia.Call("addEventListener", "change", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
fyne.CurrentApp().Settings().(*settings).setupTheme()
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
}
|
13
vendor/fyne.io/fyne/v2/app/app_theme_web.go
generated
vendored
Normal file
13
vendor/fyne.io/fyne/v2/app/app_theme_web.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
//go:build !ci && !js && !wasm && test_web_driver
|
||||
// +build !ci,!js,!wasm,test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
return theme.VariantDark
|
||||
}
|
124
vendor/fyne.io/fyne/v2/app/app_windows.go
generated
vendored
Normal file
124
vendor/fyne.io/fyne/v2/app/app_windows.go
generated
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
//go:build !ci && !js && !android && !ios && !wasm && !test_web_driver
|
||||
// +build !ci,!js,!android,!ios,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/execabs"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
const notificationTemplate = `$title = "%s"
|
||||
$content = "%s"
|
||||
|
||||
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] > $null
|
||||
$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)
|
||||
$toastXml = [xml] $template.GetXml()
|
||||
$toastXml.GetElementsByTagName("text")[0].AppendChild($toastXml.CreateTextNode($title)) > $null
|
||||
$toastXml.GetElementsByTagName("text")[1].AppendChild($toastXml.CreateTextNode($content)) > $null
|
||||
|
||||
$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
|
||||
$xml.LoadXml($toastXml.OuterXml)
|
||||
$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
|
||||
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("%s").Show($toast);`
|
||||
|
||||
func isDark() bool {
|
||||
k, err := registry.OpenKey(registry.CURRENT_USER, `SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize`, registry.QUERY_VALUE)
|
||||
if err != nil { // older version of Windows will not have this key
|
||||
return false
|
||||
}
|
||||
defer k.Close()
|
||||
|
||||
useLight, _, err := k.GetIntegerValue("AppsUseLightTheme")
|
||||
if err != nil { // older version of Windows will not have this value
|
||||
return false
|
||||
}
|
||||
|
||||
return useLight == 0
|
||||
}
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
if isDark() {
|
||||
return theme.VariantDark
|
||||
}
|
||||
return theme.VariantLight
|
||||
}
|
||||
|
||||
func rootConfigDir() string {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
|
||||
desktopConfig := filepath.Join(filepath.Join(homeDir, "AppData"), "Roaming")
|
||||
return filepath.Join(desktopConfig, "fyne")
|
||||
}
|
||||
|
||||
func (a *fyneApp) OpenURL(url *url.URL) error {
|
||||
cmd := execabs.Command("rundll32", "url.dll,FileProtocolHandler", url.String())
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
var scriptNum = 0
|
||||
|
||||
func (a *fyneApp) SendNotification(n *fyne.Notification) {
|
||||
title := escapeNotificationString(n.Title)
|
||||
content := escapeNotificationString(n.Content)
|
||||
appID := a.UniqueID()
|
||||
if appID == "" || strings.Index(appID, "missing-id") == 0 {
|
||||
appID = a.Metadata().Name
|
||||
}
|
||||
|
||||
script := fmt.Sprintf(notificationTemplate, title, content, appID)
|
||||
go runScript("notify", script)
|
||||
}
|
||||
|
||||
// SetSystemTrayMenu creates a system tray item and attaches the specified menu.
|
||||
// By default this will use the application icon.
|
||||
func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) {
|
||||
a.Driver().(systrayDriver).SetSystemTrayMenu(menu)
|
||||
}
|
||||
|
||||
// SetSystemTrayIcon sets a custom image for the system tray icon.
|
||||
// You should have previously called `SetSystemTrayMenu` to initialise the menu icon.
|
||||
func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) {
|
||||
a.Driver().(systrayDriver).SetSystemTrayIcon(icon)
|
||||
}
|
||||
|
||||
func escapeNotificationString(in string) string {
|
||||
noSlash := strings.ReplaceAll(in, "`", "``")
|
||||
return strings.ReplaceAll(noSlash, "\"", "`\"")
|
||||
}
|
||||
|
||||
func runScript(name, script string) {
|
||||
scriptNum++
|
||||
appID := fyne.CurrentApp().UniqueID()
|
||||
fileName := fmt.Sprintf("fyne-%s-%s-%d.ps1", appID, name, scriptNum)
|
||||
|
||||
tmpFilePath := filepath.Join(os.TempDir(), fileName)
|
||||
err := os.WriteFile(tmpFilePath, []byte(script), 0600)
|
||||
if err != nil {
|
||||
fyne.LogError("Could not write script to show notification", err)
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpFilePath)
|
||||
|
||||
launch := "(Get-Content -Encoding UTF8 -Path " + tmpFilePath + " -Raw) | Invoke-Expression"
|
||||
cmd := execabs.Command("PowerShell", "-ExecutionPolicy", "Bypass", launch)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to launch windows notify script", err)
|
||||
}
|
||||
}
|
||||
func watchTheme() {
|
||||
// TODO monitor the Windows theme
|
||||
}
|
202
vendor/fyne.io/fyne/v2/app/app_xdg.go
generated
vendored
Normal file
202
vendor/fyne.io/fyne/v2/app/app_xdg.go
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
//go:build !ci && !js && !wasm && !test_web_driver && (linux || openbsd || freebsd || netbsd) && !android
|
||||
// +build !ci
|
||||
// +build !js
|
||||
// +build !wasm
|
||||
// +build !test_web_driver
|
||||
// +build linux openbsd freebsd netbsd
|
||||
// +build !android
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/godbus/dbus/v5"
|
||||
"golang.org/x/sys/execabs"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
var once sync.Once
|
||||
|
||||
func defaultVariant() fyne.ThemeVariant {
|
||||
return findFreedestktopColorScheme()
|
||||
}
|
||||
|
||||
func (a *fyneApp) OpenURL(url *url.URL) error {
|
||||
cmd := execabs.Command("xdg-open", url.String())
|
||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||
return cmd.Start()
|
||||
}
|
||||
|
||||
// fetch color variant from dbus portal desktop settings.
|
||||
func findFreedestktopColorScheme() fyne.ThemeVariant {
|
||||
dbusConn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to connect to session D-Bus", err)
|
||||
return theme.VariantDark
|
||||
}
|
||||
|
||||
dbusObj := dbusConn.Object("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop")
|
||||
call := dbusObj.Call(
|
||||
"org.freedesktop.portal.Settings.Read",
|
||||
dbus.FlagNoAutoStart,
|
||||
"org.freedesktop.appearance",
|
||||
"color-scheme",
|
||||
)
|
||||
if call.Err != nil {
|
||||
// many desktops don't have this exported yet
|
||||
return theme.VariantDark
|
||||
}
|
||||
|
||||
var value uint8
|
||||
if err = call.Store(&value); err != nil {
|
||||
fyne.LogError("failed to read theme variant from D-Bus", err)
|
||||
return theme.VariantDark
|
||||
}
|
||||
|
||||
// See: https://github.com/flatpak/xdg-desktop-portal/blob/1.16.0/data/org.freedesktop.impl.portal.Settings.xml#L32-L46
|
||||
// 0: No preference
|
||||
// 1: Prefer dark appearance
|
||||
// 2: Prefer light appearance
|
||||
switch value {
|
||||
case 2:
|
||||
return theme.VariantLight
|
||||
case 1:
|
||||
return theme.VariantDark
|
||||
default:
|
||||
// Default to light theme to support Gnome's default see https://github.com/fyne-io/fyne/pull/3561
|
||||
return theme.VariantLight
|
||||
}
|
||||
}
|
||||
|
||||
func (a *fyneApp) SendNotification(n *fyne.Notification) {
|
||||
conn, err := dbus.SessionBus() // shared connection, don't close
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to connect to session D-Bus", err)
|
||||
return
|
||||
}
|
||||
|
||||
appName := fyne.CurrentApp().UniqueID()
|
||||
appIcon := a.cachedIconPath()
|
||||
timeout := int32(0) // we don't support this yet
|
||||
|
||||
obj := conn.Object("org.freedesktop.Notifications", "/org/freedesktop/Notifications")
|
||||
call := obj.Call("org.freedesktop.Notifications.Notify", 0, appName, uint32(0),
|
||||
appIcon, n.Title, n.Content, []string{}, map[string]dbus.Variant{}, timeout)
|
||||
if call.Err != nil {
|
||||
fyne.LogError("Failed to send message to bus", call.Err)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *fyneApp) saveIconToCache(dirPath, filePath string) error {
|
||||
err := os.MkdirAll(dirPath, 0700)
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to create application cache directory", err)
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to create icon file", err)
|
||||
return err
|
||||
}
|
||||
|
||||
defer file.Close()
|
||||
|
||||
if icon := a.Icon(); icon != nil {
|
||||
_, err = file.Write(icon.Content())
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to write icon contents", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *fyneApp) cachedIconPath() string {
|
||||
if a.Icon() == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
dirPath := filepath.Join(rootCacheDir(), a.UniqueID())
|
||||
filePath := filepath.Join(dirPath, "icon.png")
|
||||
once.Do(func() {
|
||||
err := a.saveIconToCache(dirPath, filePath)
|
||||
if err != nil {
|
||||
filePath = ""
|
||||
}
|
||||
})
|
||||
|
||||
return filePath
|
||||
}
|
||||
|
||||
// SetSystemTrayMenu creates a system tray item and attaches the specified menu.
|
||||
// By default this will use the application icon.
|
||||
func (a *fyneApp) SetSystemTrayMenu(menu *fyne.Menu) {
|
||||
if desk, ok := a.Driver().(systrayDriver); ok { // don't use this on mobile tag
|
||||
desk.SetSystemTrayMenu(menu)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSystemTrayIcon sets a custom image for the system tray icon.
|
||||
// You should have previously called `SetSystemTrayMenu` to initialise the menu icon.
|
||||
func (a *fyneApp) SetSystemTrayIcon(icon fyne.Resource) {
|
||||
if desk, ok := a.Driver().(systrayDriver); ok { // don't use this on mobile tag
|
||||
desk.SetSystemTrayIcon(icon)
|
||||
}
|
||||
}
|
||||
|
||||
func rootConfigDir() string {
|
||||
desktopConfig, _ := os.UserConfigDir()
|
||||
return filepath.Join(desktopConfig, "fyne")
|
||||
}
|
||||
|
||||
func rootCacheDir() string {
|
||||
desktopCache, _ := os.UserCacheDir()
|
||||
return filepath.Join(desktopCache, "fyne")
|
||||
}
|
||||
|
||||
func watchTheme() {
|
||||
go watchFreedekstopThemeChange()
|
||||
}
|
||||
|
||||
func themeChanged() {
|
||||
fyne.CurrentApp().Settings().(*settings).setupTheme()
|
||||
}
|
||||
|
||||
// connect to dbus to detect color-schem theme changes in portal settings.
|
||||
func watchFreedekstopThemeChange() {
|
||||
conn, err := dbus.SessionBus()
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to connect to session D-Bus", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := conn.AddMatchSignal(
|
||||
dbus.WithMatchObjectPath("/org/freedesktop/portal/desktop"),
|
||||
dbus.WithMatchInterface("org.freedesktop.portal.Settings"),
|
||||
dbus.WithMatchMember("SettingChanged"),
|
||||
); err != nil {
|
||||
fyne.LogError("D-Bus signal match failed", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
dbusChan := make(chan *dbus.Signal)
|
||||
conn.Signal(dbusChan)
|
||||
|
||||
for sig := range dbusChan {
|
||||
for _, v := range sig.Body {
|
||||
if v == "color-scheme" {
|
||||
themeChanged()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
vendor/fyne.io/fyne/v2/app/cloud.go
generated
vendored
Normal file
47
vendor/fyne.io/fyne/v2/app/cloud.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package app
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
func (a *fyneApp) SetCloudProvider(p fyne.CloudProvider) {
|
||||
if p == nil {
|
||||
a.cloud = nil
|
||||
return
|
||||
}
|
||||
|
||||
a.transitionCloud(p)
|
||||
}
|
||||
|
||||
func (a *fyneApp) transitionCloud(p fyne.CloudProvider) {
|
||||
if a.cloud != nil {
|
||||
a.cloud.Cleanup(a)
|
||||
}
|
||||
|
||||
err := p.Setup(a)
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to set up cloud provider "+p.ProviderName(), err)
|
||||
return
|
||||
}
|
||||
a.cloud = p
|
||||
|
||||
listeners := a.prefs.ChangeListeners()
|
||||
if pp, ok := p.(fyne.CloudProviderPreferences); ok {
|
||||
a.prefs = pp.CloudPreferences(a)
|
||||
} else {
|
||||
a.prefs = a.newDefaultPreferences()
|
||||
}
|
||||
if cloud, ok := p.(fyne.CloudProviderStorage); ok {
|
||||
a.storage = cloud.CloudStorage(a)
|
||||
} else {
|
||||
store := &store{a: a}
|
||||
store.Docs = makeStoreDocs(a.uniqueID, store)
|
||||
a.storage = store
|
||||
}
|
||||
|
||||
for _, l := range listeners {
|
||||
a.prefs.AddChangeListener(l)
|
||||
l() // assume that preferences have changed because we replaced the provider
|
||||
}
|
||||
|
||||
// after transition ensure settings listener is fired
|
||||
a.settings.apply()
|
||||
}
|
28
vendor/fyne.io/fyne/v2/app/meta.go
generated
vendored
Normal file
28
vendor/fyne.io/fyne/v2/app/meta.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
var meta = fyne.AppMetadata{
|
||||
ID: "",
|
||||
Name: "",
|
||||
Version: "0.0.1",
|
||||
Build: 1,
|
||||
Release: false,
|
||||
Custom: map[string]string{},
|
||||
}
|
||||
|
||||
// SetMetadata overrides the packaged application metadata.
|
||||
// This data can be used in many places like notifications and about screens.
|
||||
func SetMetadata(m fyne.AppMetadata) {
|
||||
meta = m
|
||||
|
||||
if meta.Custom == nil {
|
||||
meta.Custom = map[string]string{}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *fyneApp) Metadata() fyne.AppMetadata {
|
||||
return meta
|
||||
}
|
208
vendor/fyne.io/fyne/v2/app/preferences.go
generated
vendored
Normal file
208
vendor/fyne.io/fyne/v2/app/preferences.go
generated
vendored
Normal file
@ -0,0 +1,208 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal"
|
||||
)
|
||||
|
||||
type preferences struct {
|
||||
*internal.InMemoryPreferences
|
||||
|
||||
prefLock sync.RWMutex
|
||||
loadingInProgress bool
|
||||
savedRecently bool
|
||||
changedDuringSaving bool
|
||||
|
||||
app *fyneApp
|
||||
needsSaveBeforeExit bool
|
||||
}
|
||||
|
||||
// Declare conformity with Preferences interface
|
||||
var _ fyne.Preferences = (*preferences)(nil)
|
||||
|
||||
// forceImmediateSave writes preferences to file immediately, ignoring the debouncing
|
||||
// logic in the change listener. Does nothing if preferences are not backed with a file.
|
||||
func (p *preferences) forceImmediateSave() {
|
||||
if !p.needsSaveBeforeExit {
|
||||
return
|
||||
}
|
||||
err := p.save()
|
||||
if err != nil {
|
||||
fyne.LogError("Failed on force saving preferences", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *preferences) resetSavedRecently() {
|
||||
go func() {
|
||||
time.Sleep(time.Millisecond * 100) // writes are not always atomic. 10ms worked, 100 is safer.
|
||||
p.prefLock.Lock()
|
||||
p.savedRecently = false
|
||||
changedDuringSaving := p.changedDuringSaving
|
||||
p.changedDuringSaving = false
|
||||
p.prefLock.Unlock()
|
||||
|
||||
if changedDuringSaving {
|
||||
p.save()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *preferences) save() error {
|
||||
return p.saveToFile(p.storagePath())
|
||||
}
|
||||
|
||||
func (p *preferences) saveToFile(path string) error {
|
||||
p.prefLock.Lock()
|
||||
p.savedRecently = true
|
||||
p.prefLock.Unlock()
|
||||
defer p.resetSavedRecently()
|
||||
err := os.MkdirAll(filepath.Dir(path), 0700)
|
||||
if err != nil { // this is not an exists error according to docs
|
||||
return err
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
if !os.IsExist(err) {
|
||||
return err
|
||||
}
|
||||
file, err = os.Open(path) // #nosec
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer file.Close()
|
||||
encode := json.NewEncoder(file)
|
||||
|
||||
p.InMemoryPreferences.ReadValues(func(values map[string]interface{}) {
|
||||
err = encode.Encode(&values)
|
||||
})
|
||||
|
||||
err2 := file.Sync()
|
||||
if err == nil {
|
||||
err = err2
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *preferences) load() {
|
||||
err := p.loadFromFile(p.storagePath())
|
||||
if err != nil {
|
||||
fyne.LogError("Preferences load error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *preferences) loadFromFile(path string) (err error) {
|
||||
file, err := os.Open(path) // #nosec
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if r := file.Close(); r != nil && err == nil {
|
||||
err = r
|
||||
}
|
||||
}()
|
||||
decode := json.NewDecoder(file)
|
||||
|
||||
p.prefLock.Lock()
|
||||
p.loadingInProgress = true
|
||||
p.prefLock.Unlock()
|
||||
|
||||
p.InMemoryPreferences.WriteValues(func(values map[string]interface{}) {
|
||||
err = decode.Decode(&values)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
convertLists(values)
|
||||
})
|
||||
|
||||
p.prefLock.Lock()
|
||||
p.loadingInProgress = false
|
||||
p.prefLock.Unlock()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func newPreferences(app *fyneApp) *preferences {
|
||||
p := &preferences{}
|
||||
p.app = app
|
||||
p.InMemoryPreferences = internal.NewInMemoryPreferences()
|
||||
|
||||
// don't load or watch if not setup
|
||||
if app.uniqueID == "" && app.Metadata().ID == "" {
|
||||
return p
|
||||
}
|
||||
|
||||
p.needsSaveBeforeExit = true
|
||||
p.AddChangeListener(func() {
|
||||
if p != app.prefs {
|
||||
return
|
||||
}
|
||||
p.prefLock.Lock()
|
||||
shouldIgnoreChange := p.savedRecently || p.loadingInProgress
|
||||
if p.savedRecently && !p.loadingInProgress {
|
||||
p.changedDuringSaving = true
|
||||
}
|
||||
p.prefLock.Unlock()
|
||||
|
||||
if shouldIgnoreChange { // callback after loading file, or too many updates in a row
|
||||
return
|
||||
}
|
||||
|
||||
err := p.save()
|
||||
if err != nil {
|
||||
fyne.LogError("Failed on saving preferences", err)
|
||||
}
|
||||
})
|
||||
p.watch()
|
||||
return p
|
||||
}
|
||||
|
||||
func convertLists(values map[string]interface{}) {
|
||||
for k, v := range values {
|
||||
if items, ok := v.([]interface{}); ok {
|
||||
if len(items) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch items[0].(type) {
|
||||
case bool:
|
||||
bools := make([]bool, len(items))
|
||||
for i, item := range items {
|
||||
bools[i] = item.(bool)
|
||||
}
|
||||
values[k] = bools
|
||||
case float64:
|
||||
floats := make([]float64, len(items))
|
||||
for i, item := range items {
|
||||
floats[i] = item.(float64)
|
||||
}
|
||||
values[k] = floats
|
||||
case int:
|
||||
ints := make([]int, len(items))
|
||||
for i, item := range items {
|
||||
ints[i] = item.(int)
|
||||
}
|
||||
values[k] = ints
|
||||
case string:
|
||||
strings := make([]string, len(items))
|
||||
for i, item := range items {
|
||||
strings[i] = item.(string)
|
||||
}
|
||||
values[k] = strings
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
vendor/fyne.io/fyne/v2/app/preferences_android.go
generated
vendored
Normal file
21
vendor/fyne.io/fyne/v2/app/preferences_android.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
//go:build android
|
||||
// +build android
|
||||
|
||||
package app
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// storagePath returns the location of the settings storage
|
||||
func (p *preferences) storagePath() string {
|
||||
// we have no global storage, use app global instead - rootConfigDir looks up in app_mobile_and.go
|
||||
return filepath.Join(p.app.storageRoot(), "preferences.json")
|
||||
}
|
||||
|
||||
// storageRoot returns the location of the app storage
|
||||
func (a *fyneApp) storageRoot() string {
|
||||
return rootConfigDir() // we are in a sandbox, so no app ID added to this path
|
||||
}
|
||||
|
||||
func (p *preferences) watch() {
|
||||
// no-op on mobile
|
||||
}
|
24
vendor/fyne.io/fyne/v2/app/preferences_ios.go
generated
vendored
Normal file
24
vendor/fyne.io/fyne/v2/app/preferences_ios.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
//go:build ios
|
||||
// +build ios
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
)
|
||||
import "C"
|
||||
|
||||
// storagePath returns the location of the settings storage
|
||||
func (p *preferences) storagePath() string {
|
||||
ret := filepath.Join(p.app.storageRoot(), "preferences.json")
|
||||
return ret
|
||||
}
|
||||
|
||||
// storageRoot returns the location of the app storage
|
||||
func (a *fyneApp) storageRoot() string {
|
||||
return rootConfigDir() // we are in a sandbox, so no app ID added to this path
|
||||
}
|
||||
|
||||
func (p *preferences) watch() {
|
||||
// no-op on mobile
|
||||
}
|
20
vendor/fyne.io/fyne/v2/app/preferences_mobile.go
generated
vendored
Normal file
20
vendor/fyne.io/fyne/v2/app/preferences_mobile.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
//go:build mobile
|
||||
// +build mobile
|
||||
|
||||
package app
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// storagePath returns the location of the settings storage
|
||||
func (p *preferences) storagePath() string {
|
||||
return filepath.Join(p.app.storageRoot(), "preferences.json")
|
||||
}
|
||||
|
||||
// storageRoot returns the location of the app storage
|
||||
func (a *fyneApp) storageRoot() string {
|
||||
return filepath.Join(rootConfigDir(), a.UniqueID())
|
||||
}
|
||||
|
||||
func (p *preferences) watch() {
|
||||
// no-op as we are in mobile simulation mode
|
||||
}
|
29
vendor/fyne.io/fyne/v2/app/preferences_other.go
generated
vendored
Normal file
29
vendor/fyne.io/fyne/v2/app/preferences_other.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
//go:build !ios && !android && !mobile
|
||||
// +build !ios,!android,!mobile
|
||||
|
||||
package app
|
||||
|
||||
import "path/filepath"
|
||||
|
||||
// storagePath returns the location of the settings storage
|
||||
func (p *preferences) storagePath() string {
|
||||
return filepath.Join(p.app.storageRoot(), "preferences.json")
|
||||
}
|
||||
|
||||
// storageRoot returns the location of the app storage
|
||||
func (a *fyneApp) storageRoot() string {
|
||||
return filepath.Join(rootConfigDir(), a.UniqueID())
|
||||
}
|
||||
|
||||
func (p *preferences) watch() {
|
||||
watchFile(p.storagePath(), func() {
|
||||
p.prefLock.RLock()
|
||||
shouldIgnoreChange := p.savedRecently
|
||||
p.prefLock.RUnlock()
|
||||
if shouldIgnoreChange {
|
||||
return
|
||||
}
|
||||
|
||||
p.load()
|
||||
})
|
||||
}
|
168
vendor/fyne.io/fyne/v2/app/settings.go
generated
vendored
Normal file
168
vendor/fyne.io/fyne/v2/app/settings.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
var noAnimations bool // set to true at compile time if no_animations tag is passed
|
||||
|
||||
// SettingsSchema is used for loading and storing global settings
|
||||
type SettingsSchema struct {
|
||||
// these items are used for global settings load
|
||||
ThemeName string `json:"theme"`
|
||||
Scale float32 `json:"scale"`
|
||||
PrimaryColor string `json:"primary_color"`
|
||||
CloudName string `json:"cloud_name"`
|
||||
CloudConfig string `json:"cloud_config"`
|
||||
DisableAnimations bool `json:"no_animations"`
|
||||
}
|
||||
|
||||
// StoragePath returns the location of the settings storage
|
||||
func (sc *SettingsSchema) StoragePath() string {
|
||||
return filepath.Join(rootConfigDir(), "settings.json")
|
||||
}
|
||||
|
||||
// Declare conformity with Settings interface
|
||||
var _ fyne.Settings = (*settings)(nil)
|
||||
|
||||
type settings struct {
|
||||
propertyLock sync.RWMutex
|
||||
theme fyne.Theme
|
||||
themeSpecified bool
|
||||
variant fyne.ThemeVariant
|
||||
|
||||
changeListeners sync.Map // map[chan fyne.Settings]bool
|
||||
watcher interface{} // normally *fsnotify.Watcher or nil - avoid import in this file
|
||||
|
||||
schema SettingsSchema
|
||||
}
|
||||
|
||||
func (s *settings) BuildType() fyne.BuildType {
|
||||
return buildMode
|
||||
}
|
||||
|
||||
func (s *settings) PrimaryColor() string {
|
||||
s.propertyLock.RLock()
|
||||
defer s.propertyLock.RUnlock()
|
||||
return s.schema.PrimaryColor
|
||||
}
|
||||
|
||||
// OverrideTheme allows the settings app to temporarily preview different theme details.
|
||||
// Please make sure that you remember the original settings and call this again to revert the change.
|
||||
func (s *settings) OverrideTheme(theme fyne.Theme, name string) {
|
||||
s.propertyLock.Lock()
|
||||
defer s.propertyLock.Unlock()
|
||||
s.schema.PrimaryColor = name
|
||||
s.theme = theme
|
||||
}
|
||||
|
||||
func (s *settings) Theme() fyne.Theme {
|
||||
s.propertyLock.RLock()
|
||||
defer s.propertyLock.RUnlock()
|
||||
return s.theme
|
||||
}
|
||||
|
||||
func (s *settings) SetTheme(theme fyne.Theme) {
|
||||
s.themeSpecified = true
|
||||
s.applyTheme(theme, s.variant)
|
||||
}
|
||||
|
||||
func (s *settings) ShowAnimations() bool {
|
||||
return !s.schema.DisableAnimations && !noAnimations
|
||||
}
|
||||
|
||||
func (s *settings) ThemeVariant() fyne.ThemeVariant {
|
||||
return s.variant
|
||||
}
|
||||
|
||||
func (s *settings) applyTheme(theme fyne.Theme, variant fyne.ThemeVariant) {
|
||||
s.propertyLock.Lock()
|
||||
defer s.propertyLock.Unlock()
|
||||
s.variant = variant
|
||||
s.theme = theme
|
||||
s.apply()
|
||||
}
|
||||
|
||||
func (s *settings) Scale() float32 {
|
||||
s.propertyLock.RLock()
|
||||
defer s.propertyLock.RUnlock()
|
||||
if s.schema.Scale < 0.0 {
|
||||
return 1.0 // catching any really old data still using the `-1` value for "auto" scale
|
||||
}
|
||||
return s.schema.Scale
|
||||
}
|
||||
|
||||
func (s *settings) AddChangeListener(listener chan fyne.Settings) {
|
||||
s.changeListeners.Store(listener, true) // the boolean is just a dummy value here.
|
||||
}
|
||||
|
||||
func (s *settings) apply() {
|
||||
s.changeListeners.Range(func(key, _ interface{}) bool {
|
||||
listener := key.(chan fyne.Settings)
|
||||
select {
|
||||
case listener <- s:
|
||||
default:
|
||||
l := listener
|
||||
go func() { l <- s }()
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func (s *settings) fileChanged() {
|
||||
s.load()
|
||||
s.apply()
|
||||
}
|
||||
|
||||
func (s *settings) loadSystemTheme() fyne.Theme {
|
||||
path := filepath.Join(rootConfigDir(), "theme.json")
|
||||
data, err := fyne.LoadResourceFromPath(path)
|
||||
if err != nil {
|
||||
if !os.IsNotExist(err) {
|
||||
fyne.LogError("Failed to load user theme file: "+path, err)
|
||||
}
|
||||
return theme.DefaultTheme()
|
||||
}
|
||||
if data != nil && data.Content() != nil {
|
||||
th, err := theme.FromJSONReader(bytes.NewReader(data.Content()))
|
||||
if err == nil {
|
||||
return th
|
||||
}
|
||||
fyne.LogError("Failed to parse user theme file: "+path, err)
|
||||
}
|
||||
return theme.DefaultTheme()
|
||||
}
|
||||
|
||||
func (s *settings) setupTheme() {
|
||||
name := s.schema.ThemeName
|
||||
if env := os.Getenv("FYNE_THEME"); env != "" {
|
||||
name = env
|
||||
}
|
||||
|
||||
variant := defaultVariant()
|
||||
effectiveTheme := s.theme
|
||||
if !s.themeSpecified {
|
||||
effectiveTheme = s.loadSystemTheme()
|
||||
}
|
||||
switch name {
|
||||
case "light":
|
||||
variant = theme.VariantLight
|
||||
case "dark":
|
||||
variant = theme.VariantDark
|
||||
}
|
||||
|
||||
s.applyTheme(effectiveTheme, variant)
|
||||
}
|
||||
|
||||
func loadSettings() *settings {
|
||||
s := &settings{}
|
||||
s.load()
|
||||
|
||||
return s
|
||||
}
|
75
vendor/fyne.io/fyne/v2/app/settings_desktop.go
generated
vendored
Normal file
75
vendor/fyne.io/fyne/v2/app/settings_desktop.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
//go:build !android && !ios && !mobile && !js && !wasm && !test_web_driver
|
||||
// +build !android,!ios,!mobile,!js,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
func watchFileAddTarget(watcher *fsnotify.Watcher, path string) {
|
||||
dir := filepath.Dir(path)
|
||||
ensureDirExists(dir)
|
||||
|
||||
err := watcher.Add(dir)
|
||||
if err != nil {
|
||||
fyne.LogError("Settings watch error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureDirExists(dir string) {
|
||||
if stat, err := os.Stat(dir); err == nil && stat.IsDir() {
|
||||
return
|
||||
}
|
||||
|
||||
err := os.MkdirAll(dir, 0700)
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to create settings storage:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func watchFile(path string, callback func()) *fsnotify.Watcher {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to watch settings file:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
go func() {
|
||||
for event := range watcher.Events {
|
||||
if event.Op.Has(fsnotify.Remove) { // if it was deleted then watch again
|
||||
watcher.Remove(path) // fsnotify returns false positives, see https://github.com/fsnotify/fsnotify/issues/268
|
||||
|
||||
watchFileAddTarget(watcher, path)
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
err = watcher.Close()
|
||||
if err != nil {
|
||||
fyne.LogError("Settings un-watch error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
watchFileAddTarget(watcher, path)
|
||||
return watcher
|
||||
}
|
||||
|
||||
func (s *settings) watchSettings() {
|
||||
s.watcher = watchFile(s.schema.StoragePath(), s.fileChanged)
|
||||
|
||||
watchTheme()
|
||||
}
|
||||
|
||||
func (s *settings) stopWatching() {
|
||||
if s.watcher == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.watcher.(*fsnotify.Watcher).Close() // fsnotify returns false positives, see https://github.com/fsnotify/fsnotify/issues/268
|
||||
}
|
35
vendor/fyne.io/fyne/v2/app/settings_file.go
generated
vendored
Normal file
35
vendor/fyne.io/fyne/v2/app/settings_file.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
//go:build !js && !wasm && !test_web_driver
|
||||
// +build !js,!wasm,!test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
func (s *settings) load() {
|
||||
err := s.loadFromFile(s.schema.StoragePath())
|
||||
if err != nil && err != io.EOF { // we can get an EOF in windows settings writes
|
||||
fyne.LogError("Settings load error:", err)
|
||||
}
|
||||
|
||||
s.setupTheme()
|
||||
}
|
||||
|
||||
func (s *settings) loadFromFile(path string) error {
|
||||
file, err := os.Open(path) // #nosec
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
decode := json.NewDecoder(file)
|
||||
|
||||
return decode.Decode(&s.schema)
|
||||
}
|
24
vendor/fyne.io/fyne/v2/app/settings_goxjs.go
generated
vendored
Normal file
24
vendor/fyne.io/fyne/v2/app/settings_goxjs.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
//go:build js || wasm || test_web_driver
|
||||
// +build js wasm test_web_driver
|
||||
|
||||
package app
|
||||
|
||||
// TODO: #2734
|
||||
|
||||
func (s *settings) load() {
|
||||
s.setupTheme()
|
||||
s.schema.Scale = 1
|
||||
}
|
||||
|
||||
func (s *settings) loadFromFile(path string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func watchFile(path string, callback func()) {
|
||||
}
|
||||
|
||||
func (s *settings) watchSettings() {
|
||||
}
|
||||
|
||||
func (s *settings) stopWatching() {
|
||||
}
|
12
vendor/fyne.io/fyne/v2/app/settings_mobile.go
generated
vendored
Normal file
12
vendor/fyne.io/fyne/v2/app/settings_mobile.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
//go:build android || ios || mobile
|
||||
// +build android ios mobile
|
||||
|
||||
package app
|
||||
|
||||
func (s *settings) watchSettings() {
|
||||
// no-op on mobile
|
||||
}
|
||||
|
||||
func (s *settings) stopWatching() {
|
||||
// no-op on mobile
|
||||
}
|
8
vendor/fyne.io/fyne/v2/app/settings_noanimation.go
generated
vendored
Normal file
8
vendor/fyne.io/fyne/v2/app/settings_noanimation.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
//go:build no_animations
|
||||
// +build no_animations
|
||||
|
||||
package app
|
||||
|
||||
func init() {
|
||||
noAnimations = true
|
||||
}
|
27
vendor/fyne.io/fyne/v2/app/storage.go
generated
vendored
Normal file
27
vendor/fyne.io/fyne/v2/app/storage.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal"
|
||||
"fyne.io/fyne/v2/storage"
|
||||
)
|
||||
|
||||
type store struct {
|
||||
*internal.Docs
|
||||
a *fyneApp
|
||||
}
|
||||
|
||||
func (s *store) RootURI() fyne.URI {
|
||||
if s.a.UniqueID() == "" {
|
||||
fyne.LogError("Storage API requires a unique ID, use app.NewWithID()", nil)
|
||||
return storage.NewFileURI(os.TempDir())
|
||||
}
|
||||
|
||||
return storage.NewFileURI(s.a.storageRoot())
|
||||
}
|
||||
|
||||
func (s *store) docRootURI() (fyne.URI, error) {
|
||||
return storage.Child(s.RootURI(), "Documents")
|
||||
}
|
58
vendor/fyne.io/fyne/v2/canvas.go
generated
vendored
Normal file
58
vendor/fyne.io/fyne/v2/canvas.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package fyne
|
||||
|
||||
import "image"
|
||||
|
||||
// Canvas defines a graphical canvas to which a CanvasObject or Container can be added.
|
||||
// Each canvas has a scale which is automatically applied during the render process.
|
||||
type Canvas interface {
|
||||
Content() CanvasObject
|
||||
SetContent(CanvasObject)
|
||||
|
||||
Refresh(CanvasObject)
|
||||
|
||||
// Focus makes the provided item focused.
|
||||
// The item has to be added to the contents of the canvas before calling this.
|
||||
Focus(Focusable)
|
||||
// FocusNext focuses the next focusable item.
|
||||
// If no item is currently focused, the first focusable item is focused.
|
||||
// If the last focusable item is currently focused, the first focusable item is focused.
|
||||
//
|
||||
// Since: 2.0
|
||||
FocusNext()
|
||||
// FocusPrevious focuses the previous focusable item.
|
||||
// If no item is currently focused, the last focusable item is focused.
|
||||
// If the first focusable item is currently focused, the last focusable item is focused.
|
||||
//
|
||||
// Since: 2.0
|
||||
FocusPrevious()
|
||||
Unfocus()
|
||||
Focused() Focusable
|
||||
|
||||
// Size returns the current size of this canvas
|
||||
Size() Size
|
||||
// Scale returns the current scale (multiplication factor) this canvas uses to render
|
||||
// The pixel size of a CanvasObject can be found by multiplying by this value.
|
||||
Scale() float32
|
||||
|
||||
// Overlays returns the overlay stack.
|
||||
Overlays() OverlayStack
|
||||
|
||||
OnTypedRune() func(rune)
|
||||
SetOnTypedRune(func(rune))
|
||||
OnTypedKey() func(*KeyEvent)
|
||||
SetOnTypedKey(func(*KeyEvent))
|
||||
AddShortcut(shortcut Shortcut, handler func(shortcut Shortcut))
|
||||
RemoveShortcut(shortcut Shortcut)
|
||||
|
||||
Capture() image.Image
|
||||
|
||||
// PixelCoordinateForPosition returns the x and y pixel coordinate for a given position on this canvas.
|
||||
// This can be used to find absolute pixel positions or pixel offsets relative to an object top left.
|
||||
PixelCoordinateForPosition(Position) (int, int)
|
||||
|
||||
// InteractiveArea returns the position and size of the central interactive area.
|
||||
// Operating system elements may overlap the portions outside this area and widgets should avoid being outside.
|
||||
//
|
||||
// Since: 1.4
|
||||
InteractiveArea() (Position, Size)
|
||||
}
|
86
vendor/fyne.io/fyne/v2/canvas/animation.go
generated
vendored
Normal file
86
vendor/fyne.io/fyne/v2/canvas/animation.go
generated
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// DurationStandard is the time a standard interface animation will run.
|
||||
//
|
||||
// Since: 2.0
|
||||
DurationStandard = time.Millisecond * 300
|
||||
// DurationShort is the time a subtle or small transition should use.
|
||||
//
|
||||
// Since: 2.0
|
||||
DurationShort = time.Millisecond * 150
|
||||
)
|
||||
|
||||
// NewColorRGBAAnimation sets up a new animation that will transition from the start to stop Color over
|
||||
// the specified Duration. The colour transition will move linearly through the RGB colour space.
|
||||
// The content of fn should apply the color values to an object and refresh it.
|
||||
// You should call Start() on the returned animation to start it.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewColorRGBAAnimation(start, stop color.Color, d time.Duration, fn func(color.Color)) *fyne.Animation {
|
||||
r1, g1, b1, a1 := start.RGBA()
|
||||
r2, g2, b2, a2 := stop.RGBA()
|
||||
|
||||
rStart := int(r1 >> 8)
|
||||
gStart := int(g1 >> 8)
|
||||
bStart := int(b1 >> 8)
|
||||
aStart := int(a1 >> 8)
|
||||
rDelta := float32(int(r2>>8) - rStart)
|
||||
gDelta := float32(int(g2>>8) - gStart)
|
||||
bDelta := float32(int(b2>>8) - bStart)
|
||||
aDelta := float32(int(a2>>8) - aStart)
|
||||
|
||||
return &fyne.Animation{
|
||||
Duration: d,
|
||||
Tick: func(done float32) {
|
||||
fn(color.RGBA{R: scaleChannel(rStart, rDelta, done), G: scaleChannel(gStart, gDelta, done),
|
||||
B: scaleChannel(bStart, bDelta, done), A: scaleChannel(aStart, aDelta, done)})
|
||||
}}
|
||||
}
|
||||
|
||||
// NewPositionAnimation sets up a new animation that will transition from the start to stop Position over
|
||||
// the specified Duration. The content of fn should apply the position value to an object for the change
|
||||
// to be visible. You should call Start() on the returned animation to start it.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewPositionAnimation(start, stop fyne.Position, d time.Duration, fn func(fyne.Position)) *fyne.Animation {
|
||||
xDelta := float32(stop.X - start.X)
|
||||
yDelta := float32(stop.Y - start.Y)
|
||||
|
||||
return &fyne.Animation{
|
||||
Duration: d,
|
||||
Tick: func(done float32) {
|
||||
fn(fyne.NewPos(scaleVal(start.X, xDelta, done), scaleVal(start.Y, yDelta, done)))
|
||||
}}
|
||||
}
|
||||
|
||||
// NewSizeAnimation sets up a new animation that will transition from the start to stop Size over
|
||||
// the specified Duration. The content of fn should apply the size value to an object for the change
|
||||
// to be visible. You should call Start() on the returned animation to start it.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewSizeAnimation(start, stop fyne.Size, d time.Duration, fn func(fyne.Size)) *fyne.Animation {
|
||||
widthDelta := float32(stop.Width - start.Width)
|
||||
heightDelta := float32(stop.Height - start.Height)
|
||||
|
||||
return &fyne.Animation{
|
||||
Duration: d,
|
||||
Tick: func(done float32) {
|
||||
fn(fyne.NewSize(scaleVal(start.Width, widthDelta, done), scaleVal(start.Height, heightDelta, done)))
|
||||
}}
|
||||
}
|
||||
|
||||
func scaleChannel(start int, diff, done float32) uint8 {
|
||||
return uint8(start + int(diff*done))
|
||||
}
|
||||
|
||||
func scaleVal(start float32, delta, done float32) float32 {
|
||||
return start + delta*done
|
||||
}
|
100
vendor/fyne.io/fyne/v2/canvas/base.go
generated
vendored
Normal file
100
vendor/fyne.io/fyne/v2/canvas/base.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
// Package canvas contains all of the primitive CanvasObjects that make up a Fyne GUI.
|
||||
//
|
||||
// The types implemented in this package are used as building blocks in order
|
||||
// to build higher order functionality. These types are designed to be
|
||||
// non-interactive, by design. If additional functionality is required,
|
||||
// it's usually a sign that this type should be used as part of a custom
|
||||
// widget.
|
||||
package canvas // import "fyne.io/fyne/v2/canvas"
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
type baseObject struct {
|
||||
size fyne.Size // The current size of the canvas object
|
||||
position fyne.Position // The current position of the object
|
||||
Hidden bool // Is this object currently hidden
|
||||
|
||||
min fyne.Size // The minimum size this object can be
|
||||
|
||||
propertyLock sync.RWMutex
|
||||
}
|
||||
|
||||
// Hide will set this object to not be visible.
|
||||
func (o *baseObject) Hide() {
|
||||
o.propertyLock.Lock()
|
||||
defer o.propertyLock.Unlock()
|
||||
|
||||
o.Hidden = true
|
||||
}
|
||||
|
||||
// MinSize returns the specified minimum size, if set, or {1, 1} otherwise.
|
||||
func (o *baseObject) MinSize() fyne.Size {
|
||||
o.propertyLock.RLock()
|
||||
defer o.propertyLock.RUnlock()
|
||||
|
||||
if o.min.Width == 0 && o.min.Height == 0 {
|
||||
return fyne.NewSize(1, 1)
|
||||
}
|
||||
|
||||
return o.min
|
||||
}
|
||||
|
||||
// Move the object to a new position, relative to its parent.
|
||||
func (o *baseObject) Move(pos fyne.Position) {
|
||||
o.propertyLock.Lock()
|
||||
defer o.propertyLock.Unlock()
|
||||
|
||||
o.position = pos
|
||||
}
|
||||
|
||||
// Position gets the current position of this canvas object, relative to its parent.
|
||||
func (o *baseObject) Position() fyne.Position {
|
||||
o.propertyLock.RLock()
|
||||
defer o.propertyLock.RUnlock()
|
||||
|
||||
return o.position
|
||||
}
|
||||
|
||||
// Resize sets a new size for the canvas object.
|
||||
func (o *baseObject) Resize(size fyne.Size) {
|
||||
o.propertyLock.Lock()
|
||||
defer o.propertyLock.Unlock()
|
||||
|
||||
o.size = size
|
||||
}
|
||||
|
||||
// SetMinSize specifies the smallest size this object should be.
|
||||
func (o *baseObject) SetMinSize(size fyne.Size) {
|
||||
o.propertyLock.Lock()
|
||||
defer o.propertyLock.Unlock()
|
||||
|
||||
o.min = size
|
||||
}
|
||||
|
||||
// Show will set this object to be visible.
|
||||
func (o *baseObject) Show() {
|
||||
o.propertyLock.Lock()
|
||||
defer o.propertyLock.Unlock()
|
||||
|
||||
o.Hidden = false
|
||||
}
|
||||
|
||||
// Size returns the current size of this canvas object.
|
||||
func (o *baseObject) Size() fyne.Size {
|
||||
o.propertyLock.RLock()
|
||||
defer o.propertyLock.RUnlock()
|
||||
|
||||
return o.size
|
||||
}
|
||||
|
||||
// Visible returns true if this object is visible, false otherwise.
|
||||
func (o *baseObject) Visible() bool {
|
||||
o.propertyLock.RLock()
|
||||
defer o.propertyLock.RUnlock()
|
||||
|
||||
return !o.Hidden
|
||||
}
|
29
vendor/fyne.io/fyne/v2/canvas/canvas.go
generated
vendored
Normal file
29
vendor/fyne.io/fyne/v2/canvas/canvas.go
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
package canvas
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
// Refresh instructs the containing canvas to refresh the specified obj.
|
||||
func Refresh(obj fyne.CanvasObject) {
|
||||
if fyne.CurrentApp() == nil || fyne.CurrentApp().Driver() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := fyne.CurrentApp().Driver().CanvasForObject(obj)
|
||||
if c != nil {
|
||||
c.Refresh(obj)
|
||||
}
|
||||
}
|
||||
|
||||
// repaint instructs the containing canvas to redraw, even if nothing changed.
|
||||
func repaint(obj fyne.CanvasObject) {
|
||||
if fyne.CurrentApp() == nil || fyne.CurrentApp().Driver() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := fyne.CurrentApp().Driver().CanvasForObject(obj)
|
||||
if c != nil {
|
||||
if paint, ok := c.(interface{ SetDirty() }); ok {
|
||||
paint.SetDirty()
|
||||
}
|
||||
}
|
||||
}
|
90
vendor/fyne.io/fyne/v2/canvas/circle.go
generated
vendored
Normal file
90
vendor/fyne.io/fyne/v2/canvas/circle.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Circle)(nil)
|
||||
|
||||
// Circle describes a colored circle primitive in a Fyne canvas
|
||||
type Circle struct {
|
||||
Position1 fyne.Position // The current top-left position of the Circle
|
||||
Position2 fyne.Position // The current bottomright position of the Circle
|
||||
Hidden bool // Is this circle currently hidden
|
||||
|
||||
FillColor color.Color // The circle fill color
|
||||
StrokeColor color.Color // The circle stroke color
|
||||
StrokeWidth float32 // The stroke width of the circle
|
||||
}
|
||||
|
||||
// NewCircle returns a new Circle instance
|
||||
func NewCircle(color color.Color) *Circle {
|
||||
return &Circle{
|
||||
FillColor: color,
|
||||
}
|
||||
}
|
||||
|
||||
// Hide will set this circle to not be visible
|
||||
func (c *Circle) Hide() {
|
||||
c.Hidden = true
|
||||
|
||||
repaint(c)
|
||||
}
|
||||
|
||||
// MinSize for a Circle simply returns Size{1, 1} as there is no
|
||||
// explicit content
|
||||
func (c *Circle) MinSize() fyne.Size {
|
||||
return fyne.NewSize(1, 1)
|
||||
}
|
||||
|
||||
// Move the circle object to a new position, relative to its parent / canvas
|
||||
func (c *Circle) Move(pos fyne.Position) {
|
||||
size := c.Size()
|
||||
c.Position1 = pos
|
||||
c.Position2 = fyne.NewPos(c.Position1.X+size.Width, c.Position1.Y+size.Height)
|
||||
repaint(c)
|
||||
}
|
||||
|
||||
// Position gets the current top-left position of this circle object, relative to its parent / canvas
|
||||
func (c *Circle) Position() fyne.Position {
|
||||
return c.Position1
|
||||
}
|
||||
|
||||
// Refresh causes this object to be redrawn with its configured state.
|
||||
func (c *Circle) Refresh() {
|
||||
Refresh(c)
|
||||
}
|
||||
|
||||
// Resize sets a new bottom-right position for the circle object
|
||||
// If it has a stroke width this will cause it to Refresh.
|
||||
func (c *Circle) Resize(size fyne.Size) {
|
||||
if size == c.Size() {
|
||||
return
|
||||
}
|
||||
|
||||
c.Position2 = fyne.NewPos(c.Position1.X+size.Width, c.Position1.Y+size.Height)
|
||||
|
||||
Refresh(c)
|
||||
}
|
||||
|
||||
// Show will set this circle to be visible
|
||||
func (c *Circle) Show() {
|
||||
c.Hidden = false
|
||||
|
||||
c.Refresh()
|
||||
}
|
||||
|
||||
// Size returns the current size of bounding box for this circle object
|
||||
func (c *Circle) Size() fyne.Size {
|
||||
return fyne.NewSize(float32(math.Abs(float64(c.Position2.X)-float64(c.Position1.X))),
|
||||
float32(math.Abs(float64(c.Position2.Y)-float64(c.Position1.Y))))
|
||||
}
|
||||
|
||||
// Visible returns true if this circle is visible, false otherwise
|
||||
func (c *Circle) Visible() bool {
|
||||
return !c.Hidden
|
||||
}
|
212
vendor/fyne.io/fyne/v2/canvas/gradient.go
generated
vendored
Normal file
212
vendor/fyne.io/fyne/v2/canvas/gradient.go
generated
vendored
Normal file
@ -0,0 +1,212 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// LinearGradient defines a Gradient travelling straight at a given angle.
|
||||
// The only supported values for the angle are `0.0` (vertical) and `90.0` (horizontal), currently.
|
||||
type LinearGradient struct {
|
||||
baseObject
|
||||
|
||||
StartColor color.Color // The beginning color of the gradient
|
||||
EndColor color.Color // The end color of the gradient
|
||||
Angle float64 // The angle of the gradient (0/180 for vertical; 90/270 for horizontal)
|
||||
}
|
||||
|
||||
// Generate calculates an image of the gradient with the specified width and height.
|
||||
func (g *LinearGradient) Generate(iw, ih int) image.Image {
|
||||
w, h := float64(iw), float64(ih)
|
||||
var generator func(x, y float64) float64
|
||||
switch g.Angle {
|
||||
case 90: // horizontal flipped
|
||||
generator = func(x, _ float64) float64 {
|
||||
return (w - x) / w
|
||||
}
|
||||
case 270: // horizontal
|
||||
generator = func(x, _ float64) float64 {
|
||||
return x / w
|
||||
}
|
||||
case 45: // diagonal negative flipped
|
||||
generator = func(x, y float64) float64 {
|
||||
return math.Abs((w - x + y) / (w + h)) // ((w+h)-(x+h-y)) / (w+h)
|
||||
}
|
||||
case 225: // diagonal negative
|
||||
generator = func(x, y float64) float64 {
|
||||
return math.Abs((x + h - y) / (w + h))
|
||||
}
|
||||
case 135: // diagonal positive flipped
|
||||
generator = func(x, y float64) float64 {
|
||||
return math.Abs((w + h - (x + y)) / (w + h))
|
||||
}
|
||||
case 315: // diagonal positive
|
||||
generator = func(x, y float64) float64 {
|
||||
return math.Abs((x + y) / (w + h))
|
||||
}
|
||||
case 180: // vertical flipped
|
||||
generator = func(_, y float64) float64 {
|
||||
return (h - y) / h
|
||||
}
|
||||
default: // vertical
|
||||
generator = func(_, y float64) float64 {
|
||||
return y / h
|
||||
}
|
||||
}
|
||||
return computeGradient(generator, iw, ih, g.StartColor, g.EndColor)
|
||||
}
|
||||
|
||||
// Hide will set this gradient to not be visible
|
||||
func (g *LinearGradient) Hide() {
|
||||
g.baseObject.Hide()
|
||||
|
||||
repaint(g)
|
||||
}
|
||||
|
||||
// Move the gradient to a new position, relative to its parent / canvas
|
||||
func (g *LinearGradient) Move(pos fyne.Position) {
|
||||
g.baseObject.Move(pos)
|
||||
|
||||
repaint(g)
|
||||
}
|
||||
|
||||
// Refresh causes this gradient to be redrawn with its configured state.
|
||||
func (g *LinearGradient) Refresh() {
|
||||
Refresh(g)
|
||||
}
|
||||
|
||||
// RadialGradient defines a Gradient travelling radially from a center point outward.
|
||||
type RadialGradient struct {
|
||||
baseObject
|
||||
|
||||
StartColor color.Color // The beginning color of the gradient
|
||||
EndColor color.Color // The end color of the gradient
|
||||
// The offset of the center for generation of the gradient.
|
||||
// This is not a DP measure but relates to the width/height.
|
||||
// A value of 0.5 would move the center by the half width/height.
|
||||
CenterOffsetX, CenterOffsetY float64
|
||||
}
|
||||
|
||||
// Generate calculates an image of the gradient with the specified width and height.
|
||||
func (g *RadialGradient) Generate(iw, ih int) image.Image {
|
||||
w, h := float64(iw), float64(ih)
|
||||
// define center plus offset
|
||||
centerX := w/2 + w*g.CenterOffsetX
|
||||
centerY := h/2 + h*g.CenterOffsetY
|
||||
|
||||
// handle negative offsets
|
||||
var a, b float64
|
||||
if g.CenterOffsetX < 0 {
|
||||
a = w - centerX
|
||||
} else {
|
||||
a = centerX
|
||||
}
|
||||
if g.CenterOffsetY < 0 {
|
||||
b = h - centerY
|
||||
} else {
|
||||
b = centerY
|
||||
}
|
||||
|
||||
generator := func(x, y float64) float64 {
|
||||
// calculate distance from center for gradient multiplier
|
||||
dx, dy := centerX-x, centerY-y
|
||||
da := math.Sqrt(dx*dx + dy*dy*a*a/b/b)
|
||||
if da > a {
|
||||
return 1
|
||||
}
|
||||
return da / a
|
||||
}
|
||||
return computeGradient(generator, iw, ih, g.StartColor, g.EndColor)
|
||||
}
|
||||
|
||||
// Hide will set this gradient to not be visible
|
||||
func (g *RadialGradient) Hide() {
|
||||
g.baseObject.Hide()
|
||||
|
||||
repaint(g)
|
||||
}
|
||||
|
||||
// Move the gradient to a new position, relative to its parent / canvas
|
||||
func (g *RadialGradient) Move(pos fyne.Position) {
|
||||
g.baseObject.Move(pos)
|
||||
|
||||
repaint(g)
|
||||
}
|
||||
|
||||
// Refresh causes this gradient to be redrawn with its configured state.
|
||||
func (g *RadialGradient) Refresh() {
|
||||
Refresh(g)
|
||||
}
|
||||
|
||||
func calculatePixel(d float64, startColor, endColor color.Color) color.Color {
|
||||
// fetch RGBA values
|
||||
aR, aG, aB, aA := startColor.RGBA()
|
||||
bR, bG, bB, bA := endColor.RGBA()
|
||||
|
||||
// Get difference
|
||||
dR := float64(bR) - float64(aR)
|
||||
dG := float64(bG) - float64(aG)
|
||||
dB := float64(bB) - float64(aB)
|
||||
dA := float64(bA) - float64(aA)
|
||||
|
||||
// Apply gradations
|
||||
pixel := &color.RGBA64{
|
||||
R: uint16(float64(aR) + d*dR),
|
||||
B: uint16(float64(aB) + d*dB),
|
||||
G: uint16(float64(aG) + d*dG),
|
||||
A: uint16(float64(aA) + d*dA),
|
||||
}
|
||||
|
||||
return pixel
|
||||
}
|
||||
|
||||
func computeGradient(generator func(x, y float64) float64, w, h int, startColor, endColor color.Color) image.Image {
|
||||
img := image.NewNRGBA(image.Rect(0, 0, w, h))
|
||||
|
||||
if startColor == nil && endColor == nil {
|
||||
return img
|
||||
} else if startColor == nil {
|
||||
startColor = color.Transparent
|
||||
} else if endColor == nil {
|
||||
endColor = color.Transparent
|
||||
}
|
||||
|
||||
for x := 0; x < w; x++ {
|
||||
for y := 0; y < h; y++ {
|
||||
distance := generator(float64(x)+0.5, float64(y)+0.5)
|
||||
img.Set(x, y, calculatePixel(distance, startColor, endColor))
|
||||
}
|
||||
}
|
||||
return img
|
||||
}
|
||||
|
||||
// NewHorizontalGradient creates a new horizontally travelling linear gradient.
|
||||
// The start color will be at the left of the gradient and the end color will be at the right.
|
||||
func NewHorizontalGradient(start, end color.Color) *LinearGradient {
|
||||
g := &LinearGradient{StartColor: start, EndColor: end}
|
||||
g.Angle = 270
|
||||
return g
|
||||
}
|
||||
|
||||
// NewLinearGradient creates a linear gradient at the specified angle.
|
||||
// The angle parameter is the degree angle along which the gradient is calculated.
|
||||
// A NewHorizontalGradient uses 270 degrees and NewVerticalGradient is 0 degrees.
|
||||
func NewLinearGradient(start, end color.Color, angle float64) *LinearGradient {
|
||||
g := &LinearGradient{StartColor: start, EndColor: end}
|
||||
g.Angle = angle
|
||||
return g
|
||||
}
|
||||
|
||||
// NewRadialGradient creates a new radial gradient.
|
||||
func NewRadialGradient(start, end color.Color) *RadialGradient {
|
||||
return &RadialGradient{StartColor: start, EndColor: end}
|
||||
}
|
||||
|
||||
// NewVerticalGradient creates a new vertically travelling linear gradient.
|
||||
// The start color will be at the top of the gradient and the end color will be at the bottom.
|
||||
func NewVerticalGradient(start color.Color, end color.Color) *LinearGradient {
|
||||
return &LinearGradient{StartColor: start, EndColor: end}
|
||||
}
|
362
vendor/fyne.io/fyne/v2/canvas/image.go
generated
vendored
Normal file
362
vendor/fyne.io/fyne/v2/canvas/image.go
generated
vendored
Normal file
@ -0,0 +1,362 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"image"
|
||||
_ "image/jpeg" // avoid users having to import when using image widget
|
||||
_ "image/png" // avoid the same for PNG images
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal/cache"
|
||||
"fyne.io/fyne/v2/internal/scale"
|
||||
"fyne.io/fyne/v2/internal/svg"
|
||||
"fyne.io/fyne/v2/storage"
|
||||
)
|
||||
|
||||
// ImageFill defines the different type of ways an image can stretch to fill its space.
|
||||
type ImageFill int
|
||||
|
||||
const (
|
||||
// ImageFillStretch will scale the image to match the Size() values.
|
||||
// This is the default and does not maintain aspect ratio.
|
||||
ImageFillStretch ImageFill = iota
|
||||
// ImageFillContain makes the image fit within the object Size(),
|
||||
// centrally and maintaining aspect ratio.
|
||||
// There may be transparent sections top and bottom or left and right.
|
||||
ImageFillContain // (Fit)
|
||||
// ImageFillOriginal ensures that the container grows to the pixel dimensions
|
||||
// required to fit the original image. The aspect of the image will be maintained so,
|
||||
// as with ImageFillContain there may be transparent areas around the image.
|
||||
// Note that the minSize may be smaller than the image dimensions if scale > 1.
|
||||
ImageFillOriginal
|
||||
)
|
||||
|
||||
// ImageScale defines the different scaling filters used to scaling images
|
||||
type ImageScale int32
|
||||
|
||||
const (
|
||||
// ImageScaleSmooth will scale the image using ApproxBiLinear filter (or GL equivalent)
|
||||
ImageScaleSmooth ImageScale = iota
|
||||
// ImageScalePixels will scale the image using NearestNeighbor filter (or GL equivalent)
|
||||
ImageScalePixels
|
||||
// ImageScaleFastest will scale the image using hardware GPU if available
|
||||
//
|
||||
// Since: 2.0
|
||||
ImageScaleFastest
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Image)(nil)
|
||||
|
||||
// Image describes a drawable image area that can render in a Fyne canvas
|
||||
// The image may be a vector or a bitmap representation, it will fill the area.
|
||||
// The fill mode can be changed by setting FillMode to a different ImageFill.
|
||||
type Image struct {
|
||||
baseObject
|
||||
|
||||
aspect float32
|
||||
icon *svg.Decoder
|
||||
isSVG bool
|
||||
lock sync.Mutex
|
||||
|
||||
// one of the following sources will provide our image data
|
||||
File string // Load the image from a file
|
||||
Resource fyne.Resource // Load the image from an in-memory resource
|
||||
Image image.Image // Specify a loaded image to use in this canvas object
|
||||
|
||||
Translucency float64 // Set a translucency value > 0.0 to fade the image
|
||||
FillMode ImageFill // Specify how the image should expand to fill or fit the available space
|
||||
ScaleMode ImageScale // Specify the type of scaling interpolation applied to the image
|
||||
}
|
||||
|
||||
// Alpha is a convenience function that returns the alpha value for an image
|
||||
// based on its Translucency value. The result is 1.0 - Translucency.
|
||||
func (i *Image) Alpha() float64 {
|
||||
return 1.0 - i.Translucency
|
||||
}
|
||||
|
||||
// Aspect will return the original content aspect after it was last refreshed.
|
||||
//
|
||||
// Since: 2.4
|
||||
func (i *Image) Aspect() float32 {
|
||||
if i.aspect == 0 {
|
||||
i.Refresh()
|
||||
}
|
||||
return i.aspect
|
||||
}
|
||||
|
||||
// Hide will set this image to not be visible
|
||||
func (i *Image) Hide() {
|
||||
i.baseObject.Hide()
|
||||
|
||||
repaint(i)
|
||||
}
|
||||
|
||||
// MinSize returns the specified minimum size, if set, or {1, 1} otherwise.
|
||||
func (i *Image) MinSize() fyne.Size {
|
||||
if i.Image == nil || i.aspect == 0 {
|
||||
i.Refresh()
|
||||
}
|
||||
return i.baseObject.MinSize()
|
||||
}
|
||||
|
||||
// Move the image object to a new position, relative to its parent top, left corner.
|
||||
func (i *Image) Move(pos fyne.Position) {
|
||||
i.baseObject.Move(pos)
|
||||
|
||||
repaint(i)
|
||||
}
|
||||
|
||||
// Refresh causes this image to be redrawn with its configured state.
|
||||
func (i *Image) Refresh() {
|
||||
i.lock.Lock()
|
||||
defer i.lock.Unlock()
|
||||
|
||||
rc, err := i.updateReader()
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to load image", err)
|
||||
return
|
||||
}
|
||||
if rc != nil {
|
||||
rcMem := rc
|
||||
defer rcMem.Close()
|
||||
}
|
||||
|
||||
if i.File != "" || i.Resource != nil || i.Image != nil {
|
||||
r, err := i.updateAspectAndMinSize(rc)
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to load image", err)
|
||||
return
|
||||
}
|
||||
rc = io.NopCloser(r)
|
||||
}
|
||||
|
||||
if i.File != "" || i.Resource != nil {
|
||||
size := i.Size()
|
||||
width := size.Width
|
||||
height := size.Height
|
||||
|
||||
if width == 0 || height == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if i.isSVG {
|
||||
tex, err := i.renderSVG(width, height)
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to render SVG", err)
|
||||
return
|
||||
}
|
||||
i.Image = tex
|
||||
} else {
|
||||
if rc == nil {
|
||||
return
|
||||
}
|
||||
|
||||
img, _, err := image.Decode(rc)
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to render image", err)
|
||||
return
|
||||
}
|
||||
i.Image = img
|
||||
}
|
||||
}
|
||||
|
||||
Refresh(i)
|
||||
}
|
||||
|
||||
// Resize on an image will scale the content or reposition it according to FillMode.
|
||||
// It will normally cause a Refresh to ensure the pixels are recalculated.
|
||||
func (i *Image) Resize(s fyne.Size) {
|
||||
if s == i.Size() {
|
||||
return
|
||||
}
|
||||
i.baseObject.Resize(s)
|
||||
if i.FillMode == ImageFillOriginal && i.size.Height > 2 { // we can just ask for a GPU redraw to align
|
||||
Refresh(i)
|
||||
return
|
||||
}
|
||||
|
||||
i.baseObject.Resize(s)
|
||||
if i.isSVG || i.Image == nil {
|
||||
i.Refresh() // we need to rasterise at the new size
|
||||
} else {
|
||||
Refresh(i) // just re-size using GPU scaling
|
||||
}
|
||||
}
|
||||
|
||||
// NewImageFromFile creates a new image from a local file.
|
||||
// Images returned from this method will scale to fit the canvas object.
|
||||
// The method for scaling can be set using the Fill field.
|
||||
func NewImageFromFile(file string) *Image {
|
||||
return &Image{File: file}
|
||||
}
|
||||
|
||||
// NewImageFromURI creates a new image from named resource.
|
||||
// File URIs will read the file path and other schemes will download the data into a resource.
|
||||
// HTTP and HTTPs URIs will use the GET method by default to request the resource.
|
||||
// Images returned from this method will scale to fit the canvas object.
|
||||
// The method for scaling can be set using the Fill field.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewImageFromURI(uri fyne.URI) *Image {
|
||||
if uri.Scheme() == "file" && len(uri.String()) > 7 {
|
||||
return NewImageFromFile(uri.Path())
|
||||
}
|
||||
|
||||
var read io.ReadCloser
|
||||
|
||||
read, err := storage.Reader(uri) // attempt unknown / http file type
|
||||
if err != nil {
|
||||
fyne.LogError("Failed to open image URI", err)
|
||||
return &Image{}
|
||||
}
|
||||
|
||||
defer read.Close()
|
||||
return NewImageFromReader(read, filepath.Base(uri.String()))
|
||||
}
|
||||
|
||||
// NewImageFromReader creates a new image from a data stream.
|
||||
// The name parameter is required to uniquely identify this image (for caching etc.).
|
||||
// If the image in this io.Reader is an SVG, the name should end ".svg".
|
||||
// Images returned from this method will scale to fit the canvas object.
|
||||
// The method for scaling can be set using the Fill field.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewImageFromReader(read io.Reader, name string) *Image {
|
||||
data, err := io.ReadAll(read)
|
||||
if err != nil {
|
||||
fyne.LogError("Unable to read image data", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
res := &fyne.StaticResource{
|
||||
StaticName: name,
|
||||
StaticContent: data,
|
||||
}
|
||||
|
||||
return NewImageFromResource(res)
|
||||
}
|
||||
|
||||
// NewImageFromResource creates a new image by loading the specified resource.
|
||||
// Images returned from this method will scale to fit the canvas object.
|
||||
// The method for scaling can be set using the Fill field.
|
||||
func NewImageFromResource(res fyne.Resource) *Image {
|
||||
return &Image{Resource: res}
|
||||
}
|
||||
|
||||
// NewImageFromImage returns a new Image instance that is rendered from the Go
|
||||
// image.Image passed in.
|
||||
// Images returned from this method will scale to fit the canvas object.
|
||||
// The method for scaling can be set using the Fill field.
|
||||
func NewImageFromImage(img image.Image) *Image {
|
||||
return &Image{Image: img}
|
||||
}
|
||||
|
||||
func (i *Image) name() string {
|
||||
if i.Resource != nil {
|
||||
return i.Resource.Name()
|
||||
} else if i.File != "" {
|
||||
return i.File
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (i *Image) updateReader() (io.ReadCloser, error) {
|
||||
i.isSVG = false
|
||||
if i.Resource != nil {
|
||||
i.isSVG = svg.IsResourceSVG(i.Resource)
|
||||
return io.NopCloser(bytes.NewReader(i.Resource.Content())), nil
|
||||
} else if i.File != "" {
|
||||
var err error
|
||||
|
||||
fd, err := os.Open(i.File)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
i.isSVG = svg.IsFileSVG(i.File)
|
||||
return fd, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (i *Image) updateAspectAndMinSize(reader io.Reader) (io.Reader, error) {
|
||||
var pixWidth, pixHeight int
|
||||
|
||||
if reader != nil {
|
||||
r, width, height, aspect, err := i.imageDetailsFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reader = r
|
||||
i.aspect = aspect
|
||||
pixWidth, pixHeight = width, height
|
||||
} else if i.Image != nil {
|
||||
original := i.Image.Bounds().Size()
|
||||
i.aspect = float32(original.X) / float32(original.Y)
|
||||
pixWidth, pixHeight = original.X, original.Y
|
||||
} else {
|
||||
return nil, errors.New("no matching image source")
|
||||
}
|
||||
|
||||
if i.FillMode == ImageFillOriginal {
|
||||
i.SetMinSize(scale.ToFyneSize(i, pixWidth, pixHeight))
|
||||
}
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
func (i *Image) imageDetailsFromReader(source io.Reader) (reader io.Reader, width, height int, aspect float32, err error) {
|
||||
if source == nil {
|
||||
return nil, 0, 0, 0, errors.New("no matching reading reader")
|
||||
}
|
||||
|
||||
if i.isSVG {
|
||||
var err error
|
||||
|
||||
i.icon, err = svg.NewDecoder(source)
|
||||
if err != nil {
|
||||
return nil, 0, 0, 0, err
|
||||
}
|
||||
config := i.icon.Config()
|
||||
width, height = config.Width, config.Height
|
||||
aspect = config.Aspect
|
||||
} else {
|
||||
var buf bytes.Buffer
|
||||
tee := io.TeeReader(source, &buf)
|
||||
reader = io.MultiReader(&buf, source)
|
||||
|
||||
config, _, err := image.DecodeConfig(tee)
|
||||
if err != nil {
|
||||
return nil, 0, 0, 0, err
|
||||
}
|
||||
width, height = config.Width, config.Height
|
||||
aspect = float32(width) / float32(height)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (i *Image) renderSVG(width, height float32) (image.Image, error) {
|
||||
c := fyne.CurrentApp().Driver().CanvasForObject(i)
|
||||
screenWidth, screenHeight := int(width), int(height)
|
||||
if c != nil {
|
||||
// We want real output pixel count not just the screen coordinate space (i.e. macOS Retina)
|
||||
screenWidth, screenHeight = c.PixelCoordinateForPosition(fyne.Position{X: width, Y: height})
|
||||
}
|
||||
|
||||
tex := cache.GetSvg(i.name(), screenWidth, screenHeight)
|
||||
if tex != nil {
|
||||
return tex, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
tex, err = i.icon.Draw(screenWidth, screenHeight)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cache.SetSvg(i.name(), tex, screenWidth, screenHeight)
|
||||
return tex, nil
|
||||
}
|
102
vendor/fyne.io/fyne/v2/canvas/line.go
generated
vendored
Normal file
102
vendor/fyne.io/fyne/v2/canvas/line.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
"math"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Line)(nil)
|
||||
|
||||
// Line describes a colored line primitive in a Fyne canvas.
|
||||
// Lines are special as they can have a negative width or height to indicate
|
||||
// an inverse slope (i.e. slope up vs down).
|
||||
type Line struct {
|
||||
Position1 fyne.Position // The current top-left position of the Line
|
||||
Position2 fyne.Position // The current bottom-right position of the Line
|
||||
Hidden bool // Is this Line currently hidden
|
||||
|
||||
StrokeColor color.Color // The line stroke color
|
||||
StrokeWidth float32 // The stroke width of the line
|
||||
}
|
||||
|
||||
// Size returns the current size of bounding box for this line object
|
||||
func (l *Line) Size() fyne.Size {
|
||||
return fyne.NewSize(float32(math.Abs(float64(l.Position2.X)-float64(l.Position1.X))),
|
||||
float32(math.Abs(float64(l.Position2.Y)-float64(l.Position1.Y))))
|
||||
}
|
||||
|
||||
// Resize sets a new bottom-right position for the line object, then it will then be refreshed.
|
||||
func (l *Line) Resize(size fyne.Size) {
|
||||
if size == l.Size() {
|
||||
return
|
||||
}
|
||||
|
||||
if l.Position1.X <= l.Position2.X {
|
||||
l.Position2.X = l.Position1.X + size.Width
|
||||
} else {
|
||||
l.Position1.X = l.Position2.X + size.Width
|
||||
}
|
||||
if l.Position1.Y <= l.Position2.Y {
|
||||
l.Position2.Y = l.Position1.Y + size.Height
|
||||
} else {
|
||||
l.Position1.Y = l.Position2.Y + size.Height
|
||||
}
|
||||
Refresh(l)
|
||||
}
|
||||
|
||||
// Position gets the current top-left position of this line object, relative to its parent / canvas
|
||||
func (l *Line) Position() fyne.Position {
|
||||
return fyne.NewPos(fyne.Min(l.Position1.X, l.Position2.X), fyne.Min(l.Position1.Y, l.Position2.Y))
|
||||
}
|
||||
|
||||
// Move the line object to a new position, relative to its parent / canvas
|
||||
func (l *Line) Move(pos fyne.Position) {
|
||||
oldPos := l.Position()
|
||||
deltaX := pos.X - oldPos.X
|
||||
deltaY := pos.Y - oldPos.Y
|
||||
|
||||
l.Position1 = l.Position1.Add(fyne.NewPos(deltaX, deltaY))
|
||||
l.Position2 = l.Position2.Add(fyne.NewPos(deltaX, deltaY))
|
||||
repaint(l)
|
||||
}
|
||||
|
||||
// MinSize for a Line simply returns Size{1, 1} as there is no
|
||||
// explicit content
|
||||
func (l *Line) MinSize() fyne.Size {
|
||||
return fyne.NewSize(1, 1)
|
||||
}
|
||||
|
||||
// Visible returns true if this line// Show will set this circle to be visible is visible, false otherwise
|
||||
func (l *Line) Visible() bool {
|
||||
return !l.Hidden
|
||||
}
|
||||
|
||||
// Show will set this line to be visible
|
||||
func (l *Line) Show() {
|
||||
l.Hidden = false
|
||||
|
||||
l.Refresh()
|
||||
}
|
||||
|
||||
// Hide will set this line to not be visible
|
||||
func (l *Line) Hide() {
|
||||
l.Hidden = true
|
||||
|
||||
repaint(l)
|
||||
}
|
||||
|
||||
// Refresh causes this line to be redrawn with its configured state.
|
||||
func (l *Line) Refresh() {
|
||||
Refresh(l)
|
||||
}
|
||||
|
||||
// NewLine returns a new Line instance
|
||||
func NewLine(color color.Color) *Line {
|
||||
return &Line{
|
||||
StrokeColor: color,
|
||||
StrokeWidth: 1,
|
||||
}
|
||||
}
|
196
vendor/fyne.io/fyne/v2/canvas/raster.go
generated
vendored
Normal file
196
vendor/fyne.io/fyne/v2/canvas/raster.go
generated
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Raster)(nil)
|
||||
|
||||
// Raster describes a raster image area that can render in a Fyne canvas
|
||||
type Raster struct {
|
||||
baseObject
|
||||
|
||||
// Render the raster image from code
|
||||
Generator func(w, h int) image.Image
|
||||
|
||||
// Set a translucency value > 0.0 to fade the raster
|
||||
Translucency float64
|
||||
// Specify the type of scaling interpolation applied to the raster if it is not full-size
|
||||
// Since: 1.4.1
|
||||
ScaleMode ImageScale
|
||||
}
|
||||
|
||||
// Alpha is a convenience function that returns the alpha value for a raster
|
||||
// based on its Translucency value. The result is 1.0 - Translucency.
|
||||
func (r *Raster) Alpha() float64 {
|
||||
return 1.0 - r.Translucency
|
||||
}
|
||||
|
||||
// Hide will set this raster to not be visible
|
||||
func (r *Raster) Hide() {
|
||||
r.baseObject.Hide()
|
||||
|
||||
repaint(r)
|
||||
}
|
||||
|
||||
// Move the raster to a new position, relative to its parent / canvas
|
||||
func (r *Raster) Move(pos fyne.Position) {
|
||||
r.baseObject.Move(pos)
|
||||
|
||||
repaint(r)
|
||||
}
|
||||
|
||||
// Resize on a raster image causes the new size to be set and then calls Refresh.
|
||||
// This causes the underlying data to be recalculated and a new output to be drawn.
|
||||
func (r *Raster) Resize(s fyne.Size) {
|
||||
if s == r.Size() {
|
||||
return
|
||||
}
|
||||
|
||||
r.baseObject.Resize(s)
|
||||
Refresh(r)
|
||||
}
|
||||
|
||||
// Refresh causes this raster to be redrawn with its configured state.
|
||||
func (r *Raster) Refresh() {
|
||||
Refresh(r)
|
||||
}
|
||||
|
||||
// NewRaster returns a new Image instance that is rendered dynamically using
|
||||
// the specified generate function.
|
||||
// Images returned from this method should draw dynamically to fill the width
|
||||
// and height parameters passed to pixelColor.
|
||||
func NewRaster(generate func(w, h int) image.Image) *Raster {
|
||||
return &Raster{Generator: generate}
|
||||
}
|
||||
|
||||
type pixelRaster struct {
|
||||
r *Raster
|
||||
|
||||
img draw.Image
|
||||
}
|
||||
|
||||
// NewRasterWithPixels returns a new Image instance that is rendered dynamically
|
||||
// by iterating over the specified pixelColor function for each x, y pixel.
|
||||
// Images returned from this method should draw dynamically to fill the width
|
||||
// and height parameters passed to pixelColor.
|
||||
func NewRasterWithPixels(pixelColor func(x, y, w, h int) color.Color) *Raster {
|
||||
pix := &pixelRaster{}
|
||||
pix.r = &Raster{
|
||||
Generator: func(w, h int) image.Image {
|
||||
if pix.img == nil || pix.img.Bounds().Size().X != w || pix.img.Bounds().Size().Y != h {
|
||||
// raster first pixel, figure out color type
|
||||
var dst draw.Image
|
||||
rect := image.Rect(0, 0, w, h)
|
||||
switch pixelColor(0, 0, w, h).(type) {
|
||||
case color.Alpha:
|
||||
dst = image.NewAlpha(rect)
|
||||
case color.Alpha16:
|
||||
dst = image.NewAlpha16(rect)
|
||||
case color.CMYK:
|
||||
dst = image.NewCMYK(rect)
|
||||
case color.Gray:
|
||||
dst = image.NewGray(rect)
|
||||
case color.Gray16:
|
||||
dst = image.NewGray16(rect)
|
||||
case color.NRGBA:
|
||||
dst = image.NewNRGBA(rect)
|
||||
case color.NRGBA64:
|
||||
dst = image.NewNRGBA64(rect)
|
||||
case color.RGBA:
|
||||
dst = image.NewRGBA(rect)
|
||||
case color.RGBA64:
|
||||
dst = image.NewRGBA64(rect)
|
||||
default:
|
||||
dst = image.NewRGBA(rect)
|
||||
}
|
||||
pix.img = dst
|
||||
}
|
||||
|
||||
for y := 0; y < h; y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
pix.img.Set(x, y, pixelColor(x, y, w, h))
|
||||
}
|
||||
}
|
||||
|
||||
return pix.img
|
||||
},
|
||||
}
|
||||
return pix.r
|
||||
}
|
||||
|
||||
type subImg interface {
|
||||
SubImage(r image.Rectangle) image.Image
|
||||
}
|
||||
|
||||
// NewRasterFromImage returns a new Raster instance that is rendered from the Go
|
||||
// image.Image passed in.
|
||||
// Rasters returned from this method will map pixel for pixel to the screen
|
||||
// starting img.Bounds().Min pixels from the top left of the canvas object.
|
||||
// Truncates rather than scales the image.
|
||||
// If smaller than the target space, the image will be padded with zero-pixels to the target size.
|
||||
func NewRasterFromImage(img image.Image) *Raster {
|
||||
return &Raster{
|
||||
Generator: func(w int, h int) image.Image {
|
||||
bounds := img.Bounds()
|
||||
|
||||
rect := image.Rect(0, 0, w, h)
|
||||
|
||||
switch {
|
||||
case w == bounds.Max.X && h == bounds.Max.Y:
|
||||
return img
|
||||
case w >= bounds.Max.X && h >= bounds.Max.Y:
|
||||
// try quickly truncating
|
||||
if sub, ok := img.(subImg); ok {
|
||||
return sub.SubImage(image.Rectangle{
|
||||
Min: bounds.Min,
|
||||
Max: image.Point{
|
||||
X: bounds.Min.X + w,
|
||||
Y: bounds.Min.Y + h,
|
||||
},
|
||||
})
|
||||
}
|
||||
default:
|
||||
if !rect.Overlaps(bounds) {
|
||||
return image.NewUniform(color.RGBA{})
|
||||
}
|
||||
bounds = bounds.Intersect(rect)
|
||||
}
|
||||
|
||||
// respect the user's pixel format (if possible)
|
||||
var dst draw.Image
|
||||
switch i := img.(type) {
|
||||
case *image.Alpha:
|
||||
dst = image.NewAlpha(rect)
|
||||
case *image.Alpha16:
|
||||
dst = image.NewAlpha16(rect)
|
||||
case *image.CMYK:
|
||||
dst = image.NewCMYK(rect)
|
||||
case *image.Gray:
|
||||
dst = image.NewGray(rect)
|
||||
case *image.Gray16:
|
||||
dst = image.NewGray16(rect)
|
||||
case *image.NRGBA:
|
||||
dst = image.NewNRGBA(rect)
|
||||
case *image.NRGBA64:
|
||||
dst = image.NewNRGBA64(rect)
|
||||
case *image.Paletted:
|
||||
dst = image.NewPaletted(rect, i.Palette)
|
||||
case *image.RGBA:
|
||||
dst = image.NewRGBA(rect)
|
||||
case *image.RGBA64:
|
||||
dst = image.NewRGBA64(rect)
|
||||
default:
|
||||
dst = image.NewRGBA(rect)
|
||||
}
|
||||
|
||||
draw.Draw(dst, bounds, img, bounds.Min, draw.Over)
|
||||
return dst
|
||||
},
|
||||
}
|
||||
}
|
64
vendor/fyne.io/fyne/v2/canvas/rectangle.go
generated
vendored
Normal file
64
vendor/fyne.io/fyne/v2/canvas/rectangle.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Rectangle)(nil)
|
||||
|
||||
// Rectangle describes a colored rectangle primitive in a Fyne canvas
|
||||
type Rectangle struct {
|
||||
baseObject
|
||||
|
||||
FillColor color.Color // The rectangle fill color
|
||||
StrokeColor color.Color // The rectangle stroke color
|
||||
StrokeWidth float32 // The stroke width of the rectangle
|
||||
// The radius of the rectangle corners
|
||||
//
|
||||
// Since: 2.4
|
||||
CornerRadius float32
|
||||
}
|
||||
|
||||
// Hide will set this rectangle to not be visible
|
||||
func (r *Rectangle) Hide() {
|
||||
r.baseObject.Hide()
|
||||
|
||||
repaint(r)
|
||||
}
|
||||
|
||||
// Move the rectangle to a new position, relative to its parent / canvas
|
||||
func (r *Rectangle) Move(pos fyne.Position) {
|
||||
r.baseObject.Move(pos)
|
||||
|
||||
repaint(r)
|
||||
}
|
||||
|
||||
// Refresh causes this rectangle to be redrawn with its configured state.
|
||||
func (r *Rectangle) Refresh() {
|
||||
Refresh(r)
|
||||
}
|
||||
|
||||
// Resize on a rectangle updates the new size of this object.
|
||||
// If it has a stroke width this will cause it to Refresh.
|
||||
func (r *Rectangle) Resize(s fyne.Size) {
|
||||
if s == r.Size() {
|
||||
return
|
||||
}
|
||||
|
||||
r.baseObject.Resize(s)
|
||||
if r.StrokeWidth == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
Refresh(r)
|
||||
}
|
||||
|
||||
// NewRectangle returns a new Rectangle instance
|
||||
func NewRectangle(color color.Color) *Rectangle {
|
||||
return &Rectangle{
|
||||
FillColor: color,
|
||||
}
|
||||
}
|
76
vendor/fyne.io/fyne/v2/canvas/text.go
generated
vendored
Normal file
76
vendor/fyne.io/fyne/v2/canvas/text.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package canvas
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Text)(nil)
|
||||
|
||||
// Text describes a text primitive in a Fyne canvas.
|
||||
// A text object can have a style set which will apply to the whole string.
|
||||
// No formatting or text parsing will be performed
|
||||
type Text struct {
|
||||
baseObject
|
||||
Alignment fyne.TextAlign // The alignment of the text content
|
||||
|
||||
Color color.Color // The main text draw color
|
||||
Text string // The string content of this Text
|
||||
TextSize float32 // Size of the text - if the Canvas scale is 1.0 this will be equivalent to point size
|
||||
TextStyle fyne.TextStyle // The style of the text content
|
||||
}
|
||||
|
||||
// Hide will set this text to not be visible
|
||||
func (t *Text) Hide() {
|
||||
t.baseObject.Hide()
|
||||
|
||||
repaint(t)
|
||||
}
|
||||
|
||||
// MinSize returns the minimum size of this text object based on its font size and content.
|
||||
// This is normally determined by the render implementation.
|
||||
func (t *Text) MinSize() fyne.Size {
|
||||
return fyne.MeasureText(t.Text, t.TextSize, t.TextStyle)
|
||||
}
|
||||
|
||||
// Move the text to a new position, relative to its parent / canvas
|
||||
func (t *Text) Move(pos fyne.Position) {
|
||||
t.baseObject.Move(pos)
|
||||
|
||||
repaint(t)
|
||||
}
|
||||
|
||||
// Resize on a text updates the new size of this object, which may not result in a visual change, depending on alignment.
|
||||
func (t *Text) Resize(s fyne.Size) {
|
||||
if s == t.Size() {
|
||||
return
|
||||
}
|
||||
|
||||
t.baseObject.Resize(s)
|
||||
Refresh(t)
|
||||
}
|
||||
|
||||
// SetMinSize has no effect as the smallest size this canvas object can be is based on its font size and content.
|
||||
func (t *Text) SetMinSize(fyne.Size) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
// Refresh causes this text to be redrawn with its configured state.
|
||||
func (t *Text) Refresh() {
|
||||
Refresh(t)
|
||||
}
|
||||
|
||||
// NewText returns a new Text implementation
|
||||
func NewText(text string, color color.Color) *Text {
|
||||
size := float32(0)
|
||||
if fyne.CurrentApp() != nil { // nil app possible if app not started
|
||||
size = fyne.CurrentApp().Settings().Theme().Size("text") // manually name the size to avoid import loop
|
||||
}
|
||||
return &Text{
|
||||
Color: color,
|
||||
Text: text,
|
||||
TextSize: size,
|
||||
}
|
||||
}
|
107
vendor/fyne.io/fyne/v2/canvasobject.go
generated
vendored
Normal file
107
vendor/fyne.io/fyne/v2/canvasobject.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
package fyne
|
||||
|
||||
// CanvasObject describes any graphical object that can be added to a canvas.
|
||||
// Objects have a size and position that can be controlled through this API.
|
||||
// MinSize is used to determine the minimum size which this object should be displayed.
|
||||
// An object will be visible by default but can be hidden with Hide() and re-shown with Show().
|
||||
//
|
||||
// Note: If this object is controlled as part of a Layout you should not call
|
||||
// Resize(Size) or Move(Position).
|
||||
type CanvasObject interface {
|
||||
// geometry
|
||||
|
||||
// MinSize returns the minimum size this object needs to be drawn.
|
||||
MinSize() Size
|
||||
// Move moves this object to the given position relative to its parent.
|
||||
// This should only be called if your object is not in a container with a layout manager.
|
||||
Move(Position)
|
||||
// Position returns the current position of the object relative to its parent.
|
||||
Position() Position
|
||||
// Resize resizes this object to the given size.
|
||||
// This should only be called if your object is not in a container with a layout manager.
|
||||
Resize(Size)
|
||||
// Size returns the current size of this object.
|
||||
Size() Size
|
||||
|
||||
// visibility
|
||||
|
||||
// Hide hides this object.
|
||||
Hide()
|
||||
// Visible returns whether this object is visible or not.
|
||||
Visible() bool
|
||||
// Show shows this object.
|
||||
Show()
|
||||
|
||||
// Refresh must be called if this object should be redrawn because its inner state changed.
|
||||
Refresh()
|
||||
}
|
||||
|
||||
// Disableable describes any CanvasObject that can be disabled.
|
||||
// This is primarily used with objects that also implement the Tappable interface.
|
||||
type Disableable interface {
|
||||
Enable()
|
||||
Disable()
|
||||
Disabled() bool
|
||||
}
|
||||
|
||||
// DoubleTappable describes any CanvasObject that can also be double tapped.
|
||||
type DoubleTappable interface {
|
||||
DoubleTapped(*PointEvent)
|
||||
}
|
||||
|
||||
// Draggable indicates that a CanvasObject can be dragged.
|
||||
// This is used for any item that the user has indicated should be moved across the screen.
|
||||
type Draggable interface {
|
||||
Dragged(*DragEvent)
|
||||
DragEnd()
|
||||
}
|
||||
|
||||
// Focusable describes any CanvasObject that can respond to being focused.
|
||||
// It will receive the FocusGained and FocusLost events appropriately.
|
||||
// When focused it will also have TypedRune called as text is input and
|
||||
// TypedKey called when other keys are pressed.
|
||||
//
|
||||
// Note: You must not change canvas state (including overlays or focus) in FocusGained or FocusLost
|
||||
// or you would end up with a dead-lock.
|
||||
type Focusable interface {
|
||||
// FocusGained is a hook called by the focus handling logic after this object gained the focus.
|
||||
FocusGained()
|
||||
// FocusLost is a hook called by the focus handling logic after this object lost the focus.
|
||||
FocusLost()
|
||||
|
||||
// TypedRune is a hook called by the input handling logic on text input events if this object is focused.
|
||||
TypedRune(rune)
|
||||
// TypedKey is a hook called by the input handling logic on key events if this object is focused.
|
||||
TypedKey(*KeyEvent)
|
||||
}
|
||||
|
||||
// Scrollable describes any CanvasObject that can also be scrolled.
|
||||
// This is mostly used to implement the widget.ScrollContainer.
|
||||
type Scrollable interface {
|
||||
Scrolled(*ScrollEvent)
|
||||
}
|
||||
|
||||
// SecondaryTappable describes a CanvasObject that can be right-clicked or long-tapped.
|
||||
type SecondaryTappable interface {
|
||||
TappedSecondary(*PointEvent)
|
||||
}
|
||||
|
||||
// Shortcutable describes any CanvasObject that can respond to shortcut commands (quit, cut, copy, and paste).
|
||||
type Shortcutable interface {
|
||||
TypedShortcut(Shortcut)
|
||||
}
|
||||
|
||||
// Tabbable describes any object that needs to accept the Tab key presses.
|
||||
//
|
||||
// Since: 2.1
|
||||
type Tabbable interface {
|
||||
// AcceptsTab() is a hook called by the key press handling logic.
|
||||
// If it returns true then the Tab key events will be sent using TypedKey.
|
||||
AcceptsTab() bool
|
||||
}
|
||||
|
||||
// Tappable describes any CanvasObject that can also be tapped.
|
||||
// This should be implemented by buttons etc that wish to handle pointer interactions.
|
||||
type Tappable interface {
|
||||
Tapped(*PointEvent)
|
||||
}
|
9
vendor/fyne.io/fyne/v2/clipboard.go
generated
vendored
Normal file
9
vendor/fyne.io/fyne/v2/clipboard.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package fyne
|
||||
|
||||
// Clipboard represents the system clipboard interface
|
||||
type Clipboard interface {
|
||||
// Content returns the clipboard content
|
||||
Content() string
|
||||
// SetContent sets the clipboard content
|
||||
SetContent(content string)
|
||||
}
|
39
vendor/fyne.io/fyne/v2/cloud.go
generated
vendored
Normal file
39
vendor/fyne.io/fyne/v2/cloud.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package fyne
|
||||
|
||||
// CloudProvider specifies the identifying information of a cloud provider.
|
||||
// This information is mostly used by the `fyne.io/cloud ShowSettings' user flow.
|
||||
//
|
||||
// Since: 2.3
|
||||
type CloudProvider interface {
|
||||
// ProviderDescription returns a more detailed description of this cloud provider.
|
||||
ProviderDescription() string
|
||||
// ProviderIcon returns an icon resource that is associated with the given cloud service.
|
||||
ProviderIcon() Resource
|
||||
// ProviderName returns the name of this cloud provider, usually the name of the service it uses.
|
||||
ProviderName() string
|
||||
|
||||
// Cleanup is called when this provider is no longer used and should be disposed.
|
||||
// This is guaranteed to execute before a new provider is `Setup`
|
||||
Cleanup(App)
|
||||
// Setup is called when this provider is being used for the first time.
|
||||
// Returning an error will exit the cloud setup process, though it can be retried.
|
||||
Setup(App) error
|
||||
}
|
||||
|
||||
// CloudProviderPreferences interface defines the functionality that a cloud provider will include if it is capable
|
||||
// of synchronizing user preferences.
|
||||
//
|
||||
// Since: 2.3
|
||||
type CloudProviderPreferences interface {
|
||||
// CloudPreferences returns a preference provider that will sync values to the cloud this provider uses.
|
||||
CloudPreferences(App) Preferences
|
||||
}
|
||||
|
||||
// CloudProviderStorage interface defines the functionality that a cloud provider will include if it is capable
|
||||
// of synchronizing user documents.
|
||||
//
|
||||
// Since: 2.3
|
||||
type CloudProviderStorage interface {
|
||||
// CloudStorage returns a storage provider that will sync documents to the cloud this provider uses.
|
||||
CloudStorage(App) Storage
|
||||
}
|
211
vendor/fyne.io/fyne/v2/container.go
generated
vendored
Normal file
211
vendor/fyne.io/fyne/v2/container.go
generated
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
package fyne
|
||||
|
||||
import "sync"
|
||||
|
||||
// Declare conformity to CanvasObject
|
||||
var _ CanvasObject = (*Container)(nil)
|
||||
|
||||
// Container is a CanvasObject that contains a collection of child objects.
|
||||
// The layout of the children is set by the specified Layout.
|
||||
type Container struct {
|
||||
size Size // The current size of the Container
|
||||
position Position // The current position of the Container
|
||||
Hidden bool // Is this Container hidden
|
||||
|
||||
Layout Layout // The Layout algorithm for arranging child CanvasObjects
|
||||
lock sync.Mutex
|
||||
Objects []CanvasObject // The set of CanvasObjects this container holds
|
||||
}
|
||||
|
||||
// NewContainer returns a new Container instance holding the specified CanvasObjects.
|
||||
//
|
||||
// Deprecated: Use container.NewWithoutLayout() to create a container that uses manual layout.
|
||||
func NewContainer(objects ...CanvasObject) *Container {
|
||||
return NewContainerWithoutLayout(objects...)
|
||||
}
|
||||
|
||||
// NewContainerWithoutLayout returns a new Container instance holding the specified
|
||||
// CanvasObjects that are manually arranged.
|
||||
//
|
||||
// Deprecated: Use container.NewWithoutLayout() instead
|
||||
func NewContainerWithoutLayout(objects ...CanvasObject) *Container {
|
||||
ret := &Container{
|
||||
Objects: objects,
|
||||
}
|
||||
|
||||
ret.size = ret.MinSize()
|
||||
return ret
|
||||
}
|
||||
|
||||
// NewContainerWithLayout returns a new Container instance holding the specified
|
||||
// CanvasObjects which will be laid out according to the specified Layout.
|
||||
//
|
||||
// Deprecated: Use container.New() instead
|
||||
func NewContainerWithLayout(layout Layout, objects ...CanvasObject) *Container {
|
||||
ret := &Container{
|
||||
Objects: objects,
|
||||
Layout: layout,
|
||||
}
|
||||
|
||||
ret.size = layout.MinSize(objects)
|
||||
ret.layout()
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add appends the specified object to the items this container manages.
|
||||
//
|
||||
// Since: 1.4
|
||||
func (c *Container) Add(add CanvasObject) {
|
||||
if add == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
c.Objects = append(c.Objects, add)
|
||||
c.layout()
|
||||
}
|
||||
|
||||
// AddObject adds another CanvasObject to the set this Container holds.
|
||||
//
|
||||
// Deprecated: Use replacement Add() function
|
||||
func (c *Container) AddObject(o CanvasObject) {
|
||||
c.Add(o)
|
||||
}
|
||||
|
||||
// Hide sets this container, and all its children, to be not visible.
|
||||
func (c *Container) Hide() {
|
||||
if c.Hidden {
|
||||
return
|
||||
}
|
||||
|
||||
c.Hidden = true
|
||||
repaint(c)
|
||||
}
|
||||
|
||||
// MinSize calculates the minimum size of a Container.
|
||||
// This is delegated to the Layout, if specified, otherwise it will mimic MaxLayout.
|
||||
func (c *Container) MinSize() Size {
|
||||
if c.Layout != nil {
|
||||
return c.Layout.MinSize(c.Objects)
|
||||
}
|
||||
|
||||
minSize := NewSize(1, 1)
|
||||
for _, child := range c.Objects {
|
||||
minSize = minSize.Max(child.MinSize())
|
||||
}
|
||||
|
||||
return minSize
|
||||
}
|
||||
|
||||
// Move the container (and all its children) to a new position, relative to its parent.
|
||||
func (c *Container) Move(pos Position) {
|
||||
c.position = pos
|
||||
repaint(c)
|
||||
}
|
||||
|
||||
// Position gets the current position of this Container, relative to its parent.
|
||||
func (c *Container) Position() Position {
|
||||
return c.position
|
||||
}
|
||||
|
||||
// Refresh causes this object to be redrawn in it's current state
|
||||
func (c *Container) Refresh() {
|
||||
c.layout()
|
||||
|
||||
for _, child := range c.Objects {
|
||||
child.Refresh()
|
||||
}
|
||||
|
||||
// this is basically just canvas.Refresh(c) without the package loop
|
||||
o := CurrentApp().Driver().CanvasForObject(c)
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
o.Refresh(c)
|
||||
}
|
||||
|
||||
// Remove updates the contents of this container to no longer include the specified object.
|
||||
// This method is not intended to be used inside a loop, to remove all the elements.
|
||||
// It is much more efficient to call RemoveAll() instead.
|
||||
func (c *Container) Remove(rem CanvasObject) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
if len(c.Objects) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
for i, o := range c.Objects {
|
||||
if o != rem {
|
||||
continue
|
||||
}
|
||||
|
||||
removed := make([]CanvasObject, len(c.Objects)-1)
|
||||
copy(removed, c.Objects[:i])
|
||||
copy(removed[i:], c.Objects[i+1:])
|
||||
|
||||
c.Objects = removed
|
||||
c.layout()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveAll updates the contents of this container to no longer include any objects.
|
||||
//
|
||||
// Since: 2.2
|
||||
func (c *Container) RemoveAll() {
|
||||
c.Objects = nil
|
||||
c.layout()
|
||||
}
|
||||
|
||||
// Resize sets a new size for the Container.
|
||||
func (c *Container) Resize(size Size) {
|
||||
if c.size == size {
|
||||
return
|
||||
}
|
||||
|
||||
c.size = size
|
||||
c.layout()
|
||||
}
|
||||
|
||||
// Show sets this container, and all its children, to be visible.
|
||||
func (c *Container) Show() {
|
||||
if !c.Hidden {
|
||||
return
|
||||
}
|
||||
|
||||
c.Hidden = false
|
||||
}
|
||||
|
||||
// Size returns the current size of this container.
|
||||
func (c *Container) Size() Size {
|
||||
return c.size
|
||||
}
|
||||
|
||||
// Visible returns true if the container is currently visible, false otherwise.
|
||||
func (c *Container) Visible() bool {
|
||||
return !c.Hidden
|
||||
}
|
||||
|
||||
func (c *Container) layout() {
|
||||
if c.Layout == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c.Layout.Layout(c.Objects, c.size)
|
||||
}
|
||||
|
||||
// repaint instructs the containing canvas to redraw, even if nothing changed.
|
||||
// This method is a duplicate of what is in `canvas/canvas.go` to avoid a dependency loop or public API.
|
||||
func repaint(obj *Container) {
|
||||
if CurrentApp() == nil || CurrentApp().Driver() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
c := CurrentApp().Driver().CanvasForObject(obj)
|
||||
if c != nil {
|
||||
if paint, ok := c.(interface{ SetDirty() }); ok {
|
||||
paint.SetDirty()
|
||||
}
|
||||
}
|
||||
}
|
463
vendor/fyne.io/fyne/v2/container/apptabs.go
generated
vendored
Normal file
463
vendor/fyne.io/fyne/v2/container/apptabs.go
generated
vendored
Normal file
@ -0,0 +1,463 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// Declare conformity with Widget interface.
|
||||
var _ fyne.Widget = (*AppTabs)(nil)
|
||||
|
||||
// AppTabs container is used to split your application into various different areas identified by tabs.
|
||||
// The tabs contain text and/or an icon and allow the user to switch between the content specified in each TabItem.
|
||||
// Each item is represented by a button at the edge of the container.
|
||||
//
|
||||
// Since: 1.4
|
||||
type AppTabs struct {
|
||||
widget.BaseWidget
|
||||
|
||||
Items []*TabItem
|
||||
|
||||
// Deprecated: Use `OnSelected func(*TabItem)` instead.
|
||||
OnChanged func(*TabItem)
|
||||
OnSelected func(*TabItem)
|
||||
OnUnselected func(*TabItem)
|
||||
|
||||
current int
|
||||
location TabLocation
|
||||
isTransitioning bool
|
||||
|
||||
popUpMenu *widget.PopUpMenu
|
||||
}
|
||||
|
||||
// NewAppTabs creates a new tab container that allows the user to choose between different areas of an app.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewAppTabs(items ...*TabItem) *AppTabs {
|
||||
tabs := &AppTabs{}
|
||||
tabs.BaseWidget.ExtendBaseWidget(tabs)
|
||||
tabs.SetItems(items)
|
||||
return tabs
|
||||
}
|
||||
|
||||
// CreateRenderer is a private method to Fyne which links this widget to its renderer
|
||||
//
|
||||
// Implements: fyne.Widget
|
||||
func (t *AppTabs) CreateRenderer() fyne.WidgetRenderer {
|
||||
t.BaseWidget.ExtendBaseWidget(t)
|
||||
r := &appTabsRenderer{
|
||||
baseTabsRenderer: baseTabsRenderer{
|
||||
bar: &fyne.Container{},
|
||||
divider: canvas.NewRectangle(theme.ShadowColor()),
|
||||
indicator: canvas.NewRectangle(theme.PrimaryColor()),
|
||||
},
|
||||
appTabs: t,
|
||||
}
|
||||
r.action = r.buildOverflowTabsButton()
|
||||
r.tabs = t
|
||||
|
||||
// Initially setup the tab bar to only show one tab, all others will be in overflow.
|
||||
// When the widget is laid out, and we know the size, the tab bar will be updated to show as many as can fit.
|
||||
r.updateTabs(1)
|
||||
r.updateIndicator(false)
|
||||
r.applyTheme(t)
|
||||
return r
|
||||
}
|
||||
|
||||
// Append adds a new TabItem to the end of the tab bar.
|
||||
func (t *AppTabs) Append(item *TabItem) {
|
||||
t.SetItems(append(t.Items, item))
|
||||
}
|
||||
|
||||
// CurrentTab returns the currently selected TabItem.
|
||||
//
|
||||
// Deprecated: Use `AppTabs.Selected() *TabItem` instead.
|
||||
func (t *AppTabs) CurrentTab() *TabItem {
|
||||
if t.current < 0 || t.current >= len(t.Items) {
|
||||
return nil
|
||||
}
|
||||
return t.Items[t.current]
|
||||
}
|
||||
|
||||
// CurrentTabIndex returns the index of the currently selected TabItem.
|
||||
//
|
||||
// Deprecated: Use `AppTabs.SelectedIndex() int` instead.
|
||||
func (t *AppTabs) CurrentTabIndex() int {
|
||||
return t.current
|
||||
}
|
||||
|
||||
// DisableIndex disables the TabItem at the specified index.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *AppTabs) DisableIndex(i int) {
|
||||
disableIndex(t, i)
|
||||
}
|
||||
|
||||
// DisableItem disables the specified TabItem.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *AppTabs) DisableItem(item *TabItem) {
|
||||
disableItem(t, item)
|
||||
}
|
||||
|
||||
// EnableIndex enables the TabItem at the specified index.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *AppTabs) EnableIndex(i int) {
|
||||
enableIndex(t, i)
|
||||
}
|
||||
|
||||
// EnableItem enables the specified TabItem.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *AppTabs) EnableItem(item *TabItem) {
|
||||
enableItem(t, item)
|
||||
}
|
||||
|
||||
// ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.
|
||||
//
|
||||
// Deprecated: Support for extending containers is being removed
|
||||
func (t *AppTabs) ExtendBaseWidget(wid fyne.Widget) {
|
||||
t.BaseWidget.ExtendBaseWidget(wid)
|
||||
}
|
||||
|
||||
// Hide hides the widget.
|
||||
//
|
||||
// Implements: fyne.CanvasObject
|
||||
func (t *AppTabs) Hide() {
|
||||
if t.popUpMenu != nil {
|
||||
t.popUpMenu.Hide()
|
||||
t.popUpMenu = nil
|
||||
}
|
||||
t.BaseWidget.Hide()
|
||||
}
|
||||
|
||||
// MinSize returns the size that this widget should not shrink below
|
||||
//
|
||||
// Implements: fyne.CanvasObject
|
||||
func (t *AppTabs) MinSize() fyne.Size {
|
||||
t.BaseWidget.ExtendBaseWidget(t)
|
||||
return t.BaseWidget.MinSize()
|
||||
}
|
||||
|
||||
// Remove tab by value.
|
||||
func (t *AppTabs) Remove(item *TabItem) {
|
||||
removeItem(t, item)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// RemoveIndex removes tab by index.
|
||||
func (t *AppTabs) RemoveIndex(index int) {
|
||||
removeIndex(t, index)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// Select sets the specified TabItem to be selected and its content visible.
|
||||
func (t *AppTabs) Select(item *TabItem) {
|
||||
selectItem(t, item)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// SelectIndex sets the TabItem at the specific index to be selected and its content visible.
|
||||
func (t *AppTabs) SelectIndex(index int) {
|
||||
selectIndex(t, index)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// SelectTab sets the specified TabItem to be selected and its content visible.
|
||||
//
|
||||
// Deprecated: Use `AppTabs.Select(*TabItem)` instead.
|
||||
func (t *AppTabs) SelectTab(item *TabItem) {
|
||||
for i, child := range t.Items {
|
||||
if child == item {
|
||||
t.SelectTabIndex(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SelectTabIndex sets the TabItem at the specific index to be selected and its content visible.
|
||||
//
|
||||
// Deprecated: Use `AppTabs.SelectIndex(int)` instead.
|
||||
func (t *AppTabs) SelectTabIndex(index int) {
|
||||
if index < 0 || index >= len(t.Items) || t.current == index {
|
||||
return
|
||||
}
|
||||
t.current = index
|
||||
t.Refresh()
|
||||
|
||||
if t.OnChanged != nil {
|
||||
t.OnChanged(t.Items[t.current])
|
||||
}
|
||||
}
|
||||
|
||||
// Selected returns the currently selected TabItem.
|
||||
func (t *AppTabs) Selected() *TabItem {
|
||||
return selected(t)
|
||||
}
|
||||
|
||||
// SelectedIndex returns the index of the currently selected TabItem.
|
||||
func (t *AppTabs) SelectedIndex() int {
|
||||
return t.current
|
||||
}
|
||||
|
||||
// SetItems sets the containers items and refreshes.
|
||||
func (t *AppTabs) SetItems(items []*TabItem) {
|
||||
setItems(t, items)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// SetTabLocation sets the location of the tab bar
|
||||
func (t *AppTabs) SetTabLocation(l TabLocation) {
|
||||
t.location = tabsAdjustedLocation(l)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// Show this widget, if it was previously hidden
|
||||
//
|
||||
// Implements: fyne.CanvasObject
|
||||
func (t *AppTabs) Show() {
|
||||
t.BaseWidget.Show()
|
||||
t.SelectIndex(t.current)
|
||||
}
|
||||
|
||||
func (t *AppTabs) onUnselected() func(*TabItem) {
|
||||
return t.OnUnselected
|
||||
}
|
||||
|
||||
func (t *AppTabs) onSelected() func(*TabItem) {
|
||||
return func(tab *TabItem) {
|
||||
if f := t.OnChanged; f != nil {
|
||||
f(tab)
|
||||
}
|
||||
if f := t.OnSelected; f != nil {
|
||||
f(tab)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *AppTabs) items() []*TabItem {
|
||||
return t.Items
|
||||
}
|
||||
|
||||
func (t *AppTabs) selected() int {
|
||||
return t.current
|
||||
}
|
||||
|
||||
func (t *AppTabs) setItems(items []*TabItem) {
|
||||
t.Items = items
|
||||
}
|
||||
|
||||
func (t *AppTabs) setSelected(selected int) {
|
||||
t.current = selected
|
||||
}
|
||||
|
||||
func (t *AppTabs) setTransitioning(transitioning bool) {
|
||||
t.isTransitioning = transitioning
|
||||
}
|
||||
|
||||
func (t *AppTabs) tabLocation() TabLocation {
|
||||
return t.location
|
||||
}
|
||||
|
||||
func (t *AppTabs) transitioning() bool {
|
||||
return t.isTransitioning
|
||||
}
|
||||
|
||||
// Declare conformity with WidgetRenderer interface.
|
||||
var _ fyne.WidgetRenderer = (*appTabsRenderer)(nil)
|
||||
|
||||
type appTabsRenderer struct {
|
||||
baseTabsRenderer
|
||||
appTabs *AppTabs
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) Layout(size fyne.Size) {
|
||||
// Try render as many tabs as will fit, others will appear in the overflow
|
||||
if len(r.appTabs.Items) == 0 {
|
||||
r.updateTabs(0)
|
||||
} else {
|
||||
for i := len(r.appTabs.Items); i > 0; i-- {
|
||||
r.updateTabs(i)
|
||||
barMin := r.bar.MinSize()
|
||||
if r.appTabs.location == TabLocationLeading || r.appTabs.location == TabLocationTrailing {
|
||||
if barMin.Height <= size.Height {
|
||||
// Tab bar is short enough to fit
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if barMin.Width <= size.Width {
|
||||
// Tab bar is thin enough to fit
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.layout(r.appTabs, size)
|
||||
r.updateIndicator(r.appTabs.transitioning())
|
||||
if r.appTabs.transitioning() {
|
||||
r.appTabs.setTransitioning(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) MinSize() fyne.Size {
|
||||
return r.minSize(r.appTabs)
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects(r.appTabs)
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) Refresh() {
|
||||
r.Layout(r.appTabs.Size())
|
||||
|
||||
r.refresh(r.appTabs)
|
||||
|
||||
canvas.Refresh(r.appTabs)
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) buildOverflowTabsButton() (overflow *widget.Button) {
|
||||
overflow = &widget.Button{Icon: moreIcon(r.appTabs), Importance: widget.LowImportance, OnTapped: func() {
|
||||
// Show pop up containing all tabs which did not fit in the tab bar
|
||||
|
||||
itemLen, objLen := len(r.appTabs.Items), len(r.bar.Objects[0].(*fyne.Container).Objects)
|
||||
items := make([]*fyne.MenuItem, 0, itemLen-objLen)
|
||||
for i := objLen; i < itemLen; i++ {
|
||||
index := i // capture
|
||||
// FIXME MenuItem doesn't support icons (#1752)
|
||||
// FIXME MenuItem can't show if it is the currently selected tab (#1753)
|
||||
items = append(items, fyne.NewMenuItem(r.appTabs.Items[i].Text, func() {
|
||||
r.appTabs.SelectIndex(index)
|
||||
if r.appTabs.popUpMenu != nil {
|
||||
r.appTabs.popUpMenu.Hide()
|
||||
r.appTabs.popUpMenu = nil
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
r.appTabs.popUpMenu = buildPopUpMenu(r.appTabs, overflow, items)
|
||||
}}
|
||||
|
||||
return overflow
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) buildTabButtons(count int) *fyne.Container {
|
||||
buttons := &fyne.Container{}
|
||||
|
||||
var iconPos buttonIconPosition
|
||||
if fyne.CurrentDevice().IsMobile() {
|
||||
cells := count
|
||||
if cells == 0 {
|
||||
cells = 1
|
||||
}
|
||||
if r.appTabs.location == TabLocationTop || r.appTabs.location == TabLocationBottom {
|
||||
buttons.Layout = layout.NewGridLayoutWithColumns(cells)
|
||||
} else {
|
||||
buttons.Layout = layout.NewGridLayoutWithRows(cells)
|
||||
}
|
||||
iconPos = buttonIconTop
|
||||
} else if r.appTabs.location == TabLocationLeading || r.appTabs.location == TabLocationTrailing {
|
||||
buttons.Layout = layout.NewVBoxLayout()
|
||||
iconPos = buttonIconTop
|
||||
} else {
|
||||
buttons.Layout = layout.NewHBoxLayout()
|
||||
iconPos = buttonIconInline
|
||||
}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
item := r.appTabs.Items[i]
|
||||
if item.button == nil {
|
||||
item.button = &tabButton{
|
||||
onTapped: func() { r.appTabs.Select(item) },
|
||||
}
|
||||
}
|
||||
button := item.button
|
||||
button.icon = item.Icon
|
||||
button.iconPosition = iconPos
|
||||
if i == r.appTabs.current {
|
||||
button.importance = widget.HighImportance
|
||||
} else {
|
||||
button.importance = widget.MediumImportance
|
||||
}
|
||||
button.text = item.Text
|
||||
button.textAlignment = fyne.TextAlignCenter
|
||||
button.Refresh()
|
||||
buttons.Objects = append(buttons.Objects, button)
|
||||
}
|
||||
return buttons
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) updateIndicator(animate bool) {
|
||||
if r.appTabs.current < 0 {
|
||||
r.indicator.Hide()
|
||||
return
|
||||
}
|
||||
|
||||
var selectedPos fyne.Position
|
||||
var selectedSize fyne.Size
|
||||
|
||||
buttons := r.bar.Objects[0].(*fyne.Container).Objects
|
||||
if r.appTabs.current >= len(buttons) {
|
||||
if a := r.action; a != nil {
|
||||
selectedPos = a.Position()
|
||||
selectedSize = a.Size()
|
||||
}
|
||||
} else {
|
||||
selected := buttons[r.appTabs.current]
|
||||
selectedPos = selected.Position()
|
||||
selectedSize = selected.Size()
|
||||
}
|
||||
|
||||
var indicatorPos fyne.Position
|
||||
var indicatorSize fyne.Size
|
||||
|
||||
switch r.appTabs.location {
|
||||
case TabLocationTop:
|
||||
indicatorPos = fyne.NewPos(selectedPos.X, r.bar.MinSize().Height)
|
||||
indicatorSize = fyne.NewSize(selectedSize.Width, theme.Padding())
|
||||
case TabLocationLeading:
|
||||
indicatorPos = fyne.NewPos(r.bar.MinSize().Width, selectedPos.Y)
|
||||
indicatorSize = fyne.NewSize(theme.Padding(), selectedSize.Height)
|
||||
case TabLocationBottom:
|
||||
indicatorPos = fyne.NewPos(selectedPos.X, r.bar.Position().Y-theme.Padding())
|
||||
indicatorSize = fyne.NewSize(selectedSize.Width, theme.Padding())
|
||||
case TabLocationTrailing:
|
||||
indicatorPos = fyne.NewPos(r.bar.Position().X-theme.Padding(), selectedPos.Y)
|
||||
indicatorSize = fyne.NewSize(theme.Padding(), selectedSize.Height)
|
||||
}
|
||||
|
||||
r.moveIndicator(indicatorPos, indicatorSize, animate)
|
||||
}
|
||||
|
||||
func (r *appTabsRenderer) updateTabs(max int) {
|
||||
tabCount := len(r.appTabs.Items)
|
||||
|
||||
// Set overflow action
|
||||
if tabCount <= max {
|
||||
r.action.Hide()
|
||||
r.bar.Layout = layout.NewStackLayout()
|
||||
} else {
|
||||
tabCount = max
|
||||
r.action.Show()
|
||||
|
||||
// Set layout of tab bar containing tab buttons and overflow action
|
||||
if r.appTabs.location == TabLocationLeading || r.appTabs.location == TabLocationTrailing {
|
||||
r.bar.Layout = layout.NewBorderLayout(nil, r.action, nil, nil)
|
||||
} else {
|
||||
r.bar.Layout = layout.NewBorderLayout(nil, nil, nil, r.action)
|
||||
}
|
||||
}
|
||||
|
||||
buttons := r.buildTabButtons(tabCount)
|
||||
|
||||
r.bar.Objects = []fyne.CanvasObject{buttons}
|
||||
if a := r.action; a != nil {
|
||||
r.bar.Objects = append(r.bar.Objects, a)
|
||||
}
|
||||
|
||||
r.bar.Refresh()
|
||||
}
|
20
vendor/fyne.io/fyne/v2/container/container.go
generated
vendored
Normal file
20
vendor/fyne.io/fyne/v2/container/container.go
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
// Package container provides containers that are used to lay out and organise applications.
|
||||
package container
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// New returns a new Container instance holding the specified CanvasObjects which will be laid out according to the specified Layout.
|
||||
//
|
||||
// Since: 2.0
|
||||
func New(layout fyne.Layout, objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return &fyne.Container{Layout: layout, Objects: objects}
|
||||
}
|
||||
|
||||
// NewWithoutLayout returns a new Container instance holding the specified CanvasObjects that are manually arranged.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewWithoutLayout(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return &fyne.Container{Objects: objects}
|
||||
}
|
498
vendor/fyne.io/fyne/v2/container/doctabs.go
generated
vendored
Normal file
498
vendor/fyne.io/fyne/v2/container/doctabs.go
generated
vendored
Normal file
@ -0,0 +1,498 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"image/color"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// Declare conformity with Widget interface.
|
||||
var _ fyne.Widget = (*DocTabs)(nil)
|
||||
|
||||
// DocTabs container is used to display various pieces of content identified by tabs.
|
||||
// The tabs contain text and/or an icon and allow the user to switch between the content specified in each TabItem.
|
||||
// Each item is represented by a button at the edge of the container.
|
||||
//
|
||||
// Since: 2.1
|
||||
type DocTabs struct {
|
||||
widget.BaseWidget
|
||||
|
||||
Items []*TabItem
|
||||
|
||||
CreateTab func() *TabItem
|
||||
CloseIntercept func(*TabItem)
|
||||
OnClosed func(*TabItem)
|
||||
OnSelected func(*TabItem)
|
||||
OnUnselected func(*TabItem)
|
||||
|
||||
current int
|
||||
location TabLocation
|
||||
isTransitioning bool
|
||||
|
||||
popUpMenu *widget.PopUpMenu
|
||||
}
|
||||
|
||||
// NewDocTabs creates a new tab container that allows the user to choose between various pieces of content.
|
||||
//
|
||||
// Since: 2.1
|
||||
func NewDocTabs(items ...*TabItem) *DocTabs {
|
||||
tabs := &DocTabs{}
|
||||
tabs.ExtendBaseWidget(tabs)
|
||||
tabs.SetItems(items)
|
||||
return tabs
|
||||
}
|
||||
|
||||
// Append adds a new TabItem to the end of the tab bar.
|
||||
func (t *DocTabs) Append(item *TabItem) {
|
||||
t.SetItems(append(t.Items, item))
|
||||
}
|
||||
|
||||
// CreateRenderer is a private method to Fyne which links this widget to its renderer
|
||||
//
|
||||
// Implements: fyne.Widget
|
||||
func (t *DocTabs) CreateRenderer() fyne.WidgetRenderer {
|
||||
t.ExtendBaseWidget(t)
|
||||
r := &docTabsRenderer{
|
||||
baseTabsRenderer: baseTabsRenderer{
|
||||
bar: &fyne.Container{},
|
||||
divider: canvas.NewRectangle(theme.ShadowColor()),
|
||||
indicator: canvas.NewRectangle(theme.PrimaryColor()),
|
||||
},
|
||||
docTabs: t,
|
||||
scroller: NewScroll(&fyne.Container{}),
|
||||
}
|
||||
r.action = r.buildAllTabsButton()
|
||||
r.create = r.buildCreateTabsButton()
|
||||
r.tabs = t
|
||||
|
||||
r.box = NewHBox(r.create, r.action)
|
||||
r.scroller.OnScrolled = func(offset fyne.Position) {
|
||||
r.updateIndicator(false)
|
||||
}
|
||||
r.updateAllTabs()
|
||||
r.updateCreateTab()
|
||||
r.updateTabs()
|
||||
r.updateIndicator(false)
|
||||
r.applyTheme(t)
|
||||
return r
|
||||
}
|
||||
|
||||
// DisableIndex disables the TabItem at the specified index.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *DocTabs) DisableIndex(i int) {
|
||||
disableIndex(t, i)
|
||||
}
|
||||
|
||||
// DisableItem disables the specified TabItem.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *DocTabs) DisableItem(item *TabItem) {
|
||||
disableItem(t, item)
|
||||
}
|
||||
|
||||
// EnableIndex enables the TabItem at the specified index.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *DocTabs) EnableIndex(i int) {
|
||||
enableIndex(t, i)
|
||||
}
|
||||
|
||||
// EnableItem enables the specified TabItem.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (t *DocTabs) EnableItem(item *TabItem) {
|
||||
enableItem(t, item)
|
||||
}
|
||||
|
||||
// Hide hides the widget.
|
||||
//
|
||||
// Implements: fyne.CanvasObject
|
||||
func (t *DocTabs) Hide() {
|
||||
if t.popUpMenu != nil {
|
||||
t.popUpMenu.Hide()
|
||||
t.popUpMenu = nil
|
||||
}
|
||||
t.BaseWidget.Hide()
|
||||
}
|
||||
|
||||
// MinSize returns the size that this widget should not shrink below
|
||||
//
|
||||
// Implements: fyne.CanvasObject
|
||||
func (t *DocTabs) MinSize() fyne.Size {
|
||||
t.ExtendBaseWidget(t)
|
||||
return t.BaseWidget.MinSize()
|
||||
}
|
||||
|
||||
// Remove tab by value.
|
||||
func (t *DocTabs) Remove(item *TabItem) {
|
||||
removeItem(t, item)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// RemoveIndex removes tab by index.
|
||||
func (t *DocTabs) RemoveIndex(index int) {
|
||||
removeIndex(t, index)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// Select sets the specified TabItem to be selected and its content visible.
|
||||
func (t *DocTabs) Select(item *TabItem) {
|
||||
selectItem(t, item)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// SelectIndex sets the TabItem at the specific index to be selected and its content visible.
|
||||
func (t *DocTabs) SelectIndex(index int) {
|
||||
selectIndex(t, index)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// Selected returns the currently selected TabItem.
|
||||
func (t *DocTabs) Selected() *TabItem {
|
||||
return selected(t)
|
||||
}
|
||||
|
||||
// SelectedIndex returns the index of the currently selected TabItem.
|
||||
func (t *DocTabs) SelectedIndex() int {
|
||||
return t.current
|
||||
}
|
||||
|
||||
// SetItems sets the containers items and refreshes.
|
||||
func (t *DocTabs) SetItems(items []*TabItem) {
|
||||
setItems(t, items)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// SetTabLocation sets the location of the tab bar
|
||||
func (t *DocTabs) SetTabLocation(l TabLocation) {
|
||||
t.location = tabsAdjustedLocation(l)
|
||||
t.Refresh()
|
||||
}
|
||||
|
||||
// Show this widget, if it was previously hidden
|
||||
//
|
||||
// Implements: fyne.CanvasObject
|
||||
func (t *DocTabs) Show() {
|
||||
t.BaseWidget.Show()
|
||||
t.SelectIndex(t.current)
|
||||
}
|
||||
|
||||
func (t *DocTabs) close(item *TabItem) {
|
||||
if f := t.CloseIntercept; f != nil {
|
||||
f(item)
|
||||
} else {
|
||||
t.Remove(item)
|
||||
if f := t.OnClosed; f != nil {
|
||||
f(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *DocTabs) onUnselected() func(*TabItem) {
|
||||
return t.OnUnselected
|
||||
}
|
||||
|
||||
func (t *DocTabs) onSelected() func(*TabItem) {
|
||||
return t.OnSelected
|
||||
}
|
||||
|
||||
func (t *DocTabs) items() []*TabItem {
|
||||
return t.Items
|
||||
}
|
||||
|
||||
func (t *DocTabs) selected() int {
|
||||
return t.current
|
||||
}
|
||||
|
||||
func (t *DocTabs) setItems(items []*TabItem) {
|
||||
t.Items = items
|
||||
}
|
||||
|
||||
func (t *DocTabs) setSelected(selected int) {
|
||||
t.current = selected
|
||||
}
|
||||
|
||||
func (t *DocTabs) setTransitioning(transitioning bool) {
|
||||
t.isTransitioning = transitioning
|
||||
}
|
||||
|
||||
func (t *DocTabs) tabLocation() TabLocation {
|
||||
return t.location
|
||||
}
|
||||
|
||||
func (t *DocTabs) transitioning() bool {
|
||||
return t.isTransitioning
|
||||
}
|
||||
|
||||
// Declare conformity with WidgetRenderer interface.
|
||||
var _ fyne.WidgetRenderer = (*docTabsRenderer)(nil)
|
||||
|
||||
type docTabsRenderer struct {
|
||||
baseTabsRenderer
|
||||
docTabs *DocTabs
|
||||
scroller *Scroll
|
||||
box *fyne.Container
|
||||
create *widget.Button
|
||||
lastSelected int
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) Layout(size fyne.Size) {
|
||||
r.updateAllTabs()
|
||||
r.updateCreateTab()
|
||||
r.updateTabs()
|
||||
r.layout(r.docTabs, size)
|
||||
|
||||
// lay out buttons before updating indicator, which is relative to their position
|
||||
buttons := r.scroller.Content.(*fyne.Container)
|
||||
buttons.Layout.Layout(buttons.Objects, buttons.Size())
|
||||
r.updateIndicator(r.docTabs.transitioning())
|
||||
|
||||
if r.docTabs.transitioning() {
|
||||
r.docTabs.setTransitioning(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) MinSize() fyne.Size {
|
||||
return r.minSize(r.docTabs)
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects(r.docTabs)
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) Refresh() {
|
||||
r.Layout(r.docTabs.Size())
|
||||
|
||||
if c := r.docTabs.current; c != r.lastSelected {
|
||||
if c >= 0 && c < len(r.docTabs.Items) {
|
||||
r.scrollToSelected()
|
||||
}
|
||||
r.lastSelected = c
|
||||
}
|
||||
|
||||
r.refresh(r.docTabs)
|
||||
|
||||
canvas.Refresh(r.docTabs)
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) buildAllTabsButton() (all *widget.Button) {
|
||||
all = &widget.Button{Importance: widget.LowImportance, OnTapped: func() {
|
||||
// Show pop up containing all tabs
|
||||
|
||||
items := make([]*fyne.MenuItem, len(r.docTabs.Items))
|
||||
for i := 0; i < len(r.docTabs.Items); i++ {
|
||||
index := i // capture
|
||||
// FIXME MenuItem doesn't support icons (#1752)
|
||||
items[i] = fyne.NewMenuItem(r.docTabs.Items[i].Text, func() {
|
||||
r.docTabs.SelectIndex(index)
|
||||
if r.docTabs.popUpMenu != nil {
|
||||
r.docTabs.popUpMenu.Hide()
|
||||
r.docTabs.popUpMenu = nil
|
||||
}
|
||||
})
|
||||
items[i].Checked = index == r.docTabs.current
|
||||
}
|
||||
|
||||
r.docTabs.popUpMenu = buildPopUpMenu(r.docTabs, all, items)
|
||||
}}
|
||||
|
||||
return all
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) buildCreateTabsButton() *widget.Button {
|
||||
create := widget.NewButton("", func() {
|
||||
if f := r.docTabs.CreateTab; f != nil {
|
||||
if tab := f(); tab != nil {
|
||||
r.docTabs.Append(tab)
|
||||
r.docTabs.SelectIndex(len(r.docTabs.Items) - 1)
|
||||
}
|
||||
}
|
||||
})
|
||||
create.Importance = widget.LowImportance
|
||||
return create
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) buildTabButtons(count int, buttons *fyne.Container) {
|
||||
buttons.Objects = nil
|
||||
|
||||
var iconPos buttonIconPosition
|
||||
if fyne.CurrentDevice().IsMobile() {
|
||||
cells := count
|
||||
if cells == 0 {
|
||||
cells = 1
|
||||
}
|
||||
if r.docTabs.location == TabLocationTop || r.docTabs.location == TabLocationBottom {
|
||||
buttons.Layout = layout.NewGridLayoutWithColumns(cells)
|
||||
} else {
|
||||
buttons.Layout = layout.NewGridLayoutWithRows(cells)
|
||||
}
|
||||
iconPos = buttonIconTop
|
||||
} else if r.docTabs.location == TabLocationLeading || r.docTabs.location == TabLocationTrailing {
|
||||
buttons.Layout = layout.NewVBoxLayout()
|
||||
iconPos = buttonIconTop
|
||||
} else {
|
||||
buttons.Layout = layout.NewHBoxLayout()
|
||||
iconPos = buttonIconInline
|
||||
}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
item := r.docTabs.Items[i]
|
||||
if item.button == nil {
|
||||
item.button = &tabButton{
|
||||
onTapped: func() { r.docTabs.Select(item) },
|
||||
onClosed: func() { r.docTabs.close(item) },
|
||||
}
|
||||
}
|
||||
button := item.button
|
||||
button.icon = item.Icon
|
||||
button.iconPosition = iconPos
|
||||
if i == r.docTabs.current {
|
||||
button.importance = widget.HighImportance
|
||||
} else {
|
||||
button.importance = widget.MediumImportance
|
||||
}
|
||||
button.text = item.Text
|
||||
button.textAlignment = fyne.TextAlignLeading
|
||||
button.Refresh()
|
||||
buttons.Objects = append(buttons.Objects, button)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) scrollToSelected() {
|
||||
buttons := r.scroller.Content.(*fyne.Container)
|
||||
|
||||
// https://github.com/fyne-io/fyne/issues/3909
|
||||
// very dirty temporary fix to this crash!
|
||||
if r.docTabs.current < 0 || r.docTabs.current >= len(buttons.Objects) {
|
||||
return
|
||||
}
|
||||
|
||||
button := buttons.Objects[r.docTabs.current]
|
||||
pos := button.Position()
|
||||
size := button.Size()
|
||||
offset := r.scroller.Offset
|
||||
viewport := r.scroller.Size()
|
||||
if r.docTabs.location == TabLocationLeading || r.docTabs.location == TabLocationTrailing {
|
||||
if pos.Y < offset.Y {
|
||||
offset.Y = pos.Y
|
||||
} else if pos.Y+size.Height > offset.Y+viewport.Height {
|
||||
offset.Y = pos.Y + size.Height - viewport.Height
|
||||
}
|
||||
} else {
|
||||
if pos.X < offset.X {
|
||||
offset.X = pos.X
|
||||
} else if pos.X+size.Width > offset.X+viewport.Width {
|
||||
offset.X = pos.X + size.Width - viewport.Width
|
||||
}
|
||||
}
|
||||
r.scroller.Offset = offset
|
||||
r.updateIndicator(false)
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) updateIndicator(animate bool) {
|
||||
if r.docTabs.current < 0 {
|
||||
r.indicator.FillColor = color.Transparent
|
||||
r.moveIndicator(fyne.NewPos(0, 0), fyne.NewSize(0, 0), animate)
|
||||
return
|
||||
}
|
||||
|
||||
var selectedPos fyne.Position
|
||||
var selectedSize fyne.Size
|
||||
|
||||
buttons := r.scroller.Content.(*fyne.Container).Objects
|
||||
|
||||
if r.docTabs.current >= len(buttons) {
|
||||
if a := r.action; a != nil {
|
||||
selectedPos = a.Position()
|
||||
selectedSize = a.Size()
|
||||
minSize := a.MinSize()
|
||||
if minSize.Width > selectedSize.Width {
|
||||
selectedSize = minSize
|
||||
}
|
||||
}
|
||||
} else {
|
||||
selected := buttons[r.docTabs.current]
|
||||
selectedPos = selected.Position()
|
||||
selectedSize = selected.Size()
|
||||
minSize := selected.MinSize()
|
||||
if minSize.Width > selectedSize.Width {
|
||||
selectedSize = minSize
|
||||
}
|
||||
}
|
||||
|
||||
scrollOffset := r.scroller.Offset
|
||||
scrollSize := r.scroller.Size()
|
||||
|
||||
var indicatorPos fyne.Position
|
||||
var indicatorSize fyne.Size
|
||||
|
||||
switch r.docTabs.location {
|
||||
case TabLocationTop:
|
||||
indicatorPos = fyne.NewPos(selectedPos.X-scrollOffset.X, r.bar.MinSize().Height)
|
||||
indicatorSize = fyne.NewSize(fyne.Min(selectedSize.Width, scrollSize.Width-indicatorPos.X), theme.Padding())
|
||||
case TabLocationLeading:
|
||||
indicatorPos = fyne.NewPos(r.bar.MinSize().Width, selectedPos.Y-scrollOffset.Y)
|
||||
indicatorSize = fyne.NewSize(theme.Padding(), fyne.Min(selectedSize.Height, scrollSize.Height-indicatorPos.Y))
|
||||
case TabLocationBottom:
|
||||
indicatorPos = fyne.NewPos(selectedPos.X-scrollOffset.X, r.bar.Position().Y-theme.Padding())
|
||||
indicatorSize = fyne.NewSize(fyne.Min(selectedSize.Width, scrollSize.Width-indicatorPos.X), theme.Padding())
|
||||
case TabLocationTrailing:
|
||||
indicatorPos = fyne.NewPos(r.bar.Position().X-theme.Padding(), selectedPos.Y-scrollOffset.Y)
|
||||
indicatorSize = fyne.NewSize(theme.Padding(), fyne.Min(selectedSize.Height, scrollSize.Height-indicatorPos.Y))
|
||||
}
|
||||
|
||||
if indicatorPos.X < 0 {
|
||||
indicatorSize.Width = indicatorSize.Width + indicatorPos.X
|
||||
indicatorPos.X = 0
|
||||
}
|
||||
if indicatorPos.Y < 0 {
|
||||
indicatorSize.Height = indicatorSize.Height + indicatorPos.Y
|
||||
indicatorPos.Y = 0
|
||||
}
|
||||
if indicatorSize.Width < 0 || indicatorSize.Height < 0 {
|
||||
r.indicator.FillColor = color.Transparent
|
||||
r.indicator.Refresh()
|
||||
return
|
||||
}
|
||||
|
||||
r.moveIndicator(indicatorPos, indicatorSize, animate)
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) updateAllTabs() {
|
||||
if len(r.docTabs.Items) > 0 {
|
||||
r.action.Show()
|
||||
} else {
|
||||
r.action.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) updateCreateTab() {
|
||||
if r.docTabs.CreateTab != nil {
|
||||
r.create.SetIcon(theme.ContentAddIcon())
|
||||
r.create.Show()
|
||||
} else {
|
||||
r.create.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *docTabsRenderer) updateTabs() {
|
||||
tabCount := len(r.docTabs.Items)
|
||||
r.buildTabButtons(tabCount, r.scroller.Content.(*fyne.Container))
|
||||
|
||||
// Set layout of tab bar containing tab buttons and overflow action
|
||||
if r.docTabs.location == TabLocationLeading || r.docTabs.location == TabLocationTrailing {
|
||||
r.bar.Layout = layout.NewBorderLayout(nil, r.box, nil, nil)
|
||||
r.scroller.Direction = ScrollVerticalOnly
|
||||
} else {
|
||||
r.bar.Layout = layout.NewBorderLayout(nil, nil, nil, r.box)
|
||||
r.scroller.Direction = ScrollHorizontalOnly
|
||||
}
|
||||
|
||||
r.bar.Objects = []fyne.CanvasObject{r.scroller, r.box}
|
||||
r.bar.Refresh()
|
||||
}
|
121
vendor/fyne.io/fyne/v2/container/layouts.go
generated
vendored
Normal file
121
vendor/fyne.io/fyne/v2/container/layouts.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package container // import "fyne.io/fyne/v2/container"
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal"
|
||||
"fyne.io/fyne/v2/layout"
|
||||
)
|
||||
|
||||
// NewAdaptiveGrid creates a new container with the specified objects and using the grid layout.
|
||||
// When in a horizontal arrangement the rowcols parameter will specify the column count, when in vertical
|
||||
// it will specify the rows. On mobile this will dynamically refresh when device is rotated.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewAdaptiveGrid(rowcols int, objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewAdaptiveGridLayout(rowcols), objects...)
|
||||
}
|
||||
|
||||
// NewBorder creates a new container with the specified objects and using the border layout.
|
||||
// The top, bottom, left and right parameters specify the items that should be placed around edges,
|
||||
// the remaining elements will be in the center. Nil can be used to an edge if it should not be filled.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewBorder(top, bottom, left, right fyne.CanvasObject, objects ...fyne.CanvasObject) *fyne.Container {
|
||||
all := objects
|
||||
if top != nil {
|
||||
all = append(all, top)
|
||||
}
|
||||
if bottom != nil {
|
||||
all = append(all, bottom)
|
||||
}
|
||||
if left != nil {
|
||||
all = append(all, left)
|
||||
}
|
||||
if right != nil {
|
||||
all = append(all, right)
|
||||
}
|
||||
|
||||
if len(objects) == 1 && objects[0] == nil {
|
||||
internal.LogHint("Border layout requires only 4 parameters, optional items cannot be nil")
|
||||
all = all[1:]
|
||||
}
|
||||
return New(layout.NewBorderLayout(top, bottom, left, right), all...)
|
||||
}
|
||||
|
||||
// NewCenter creates a new container with the specified objects centered in the available space.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewCenter(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewCenterLayout(), objects...)
|
||||
}
|
||||
|
||||
// NewGridWithColumns creates a new container with the specified objects and using the grid layout with
|
||||
// a specified number of columns. The number of rows will depend on how many children are in the container.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewGridWithColumns(cols int, objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewGridLayoutWithColumns(cols), objects...)
|
||||
}
|
||||
|
||||
// NewGridWithRows creates a new container with the specified objects and using the grid layout with
|
||||
// a specified number of rows. The number of columns will depend on how many children are in the container.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewGridWithRows(rows int, objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewGridLayoutWithRows(rows), objects...)
|
||||
}
|
||||
|
||||
// NewGridWrap creates a new container with the specified objects and using the gridwrap layout.
|
||||
// Every element will be resized to the size parameter and the content will arrange along a row and flow to a
|
||||
// new row if the elements don't fit.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewGridWrap(size fyne.Size, objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewGridWrapLayout(size), objects...)
|
||||
}
|
||||
|
||||
// NewHBox creates a new container with the specified objects and using the HBox layout.
|
||||
// The objects will be placed in the container from left to right and always displayed
|
||||
// at their horizontal MinSize. Use a different layout if the objects are intended
|
||||
// to be larger then their horizontal MinSize.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewHBox(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewHBoxLayout(), objects...)
|
||||
}
|
||||
|
||||
// NewMax creates a new container with the specified objects filling the available space.
|
||||
//
|
||||
// Since: 1.4
|
||||
//
|
||||
// Deprecated: Use container.NewStack() instead.
|
||||
func NewMax(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return NewStack(objects...)
|
||||
}
|
||||
|
||||
// NewPadded creates a new container with the specified objects inset by standard padding size.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewPadded(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewPaddedLayout(), objects...)
|
||||
}
|
||||
|
||||
// NewStack returns a new container that stacks objects on top of each other.
|
||||
// Objects at the end of the container will be stacked on top of objects before.
|
||||
// Having only a single object has no impact as CanvasObjects will
|
||||
// fill the available space even without a Stack.
|
||||
//
|
||||
// Since: 2.4
|
||||
func NewStack(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewStackLayout(), objects...)
|
||||
}
|
||||
|
||||
// NewVBox creates a new container with the specified objects and using the VBox layout.
|
||||
// The objects will be stacked in the container from top to bottom and always displayed
|
||||
// at their vertical MinSize. Use a different layout if the objects are intended
|
||||
// to be larger then their vertical MinSize.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewVBox(objects ...fyne.CanvasObject) *fyne.Container {
|
||||
return New(layout.NewVBoxLayout(), objects...)
|
||||
}
|
55
vendor/fyne.io/fyne/v2/container/scroll.go
generated
vendored
Normal file
55
vendor/fyne.io/fyne/v2/container/scroll.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/internal/widget"
|
||||
)
|
||||
|
||||
// Scroll defines a container that is smaller than the Content.
|
||||
// The Offset is used to determine the position of the child widgets within the container.
|
||||
//
|
||||
// Since: 1.4
|
||||
type Scroll = widget.Scroll
|
||||
|
||||
// ScrollDirection represents the directions in which a Scroll container can scroll its child content.
|
||||
//
|
||||
// Since: 1.4
|
||||
type ScrollDirection = widget.ScrollDirection
|
||||
|
||||
// Constants for valid values of ScrollDirection.
|
||||
const (
|
||||
// ScrollBoth supports horizontal and vertical scrolling.
|
||||
ScrollBoth ScrollDirection = widget.ScrollBoth
|
||||
// ScrollHorizontalOnly specifies the scrolling should only happen left to right.
|
||||
ScrollHorizontalOnly = widget.ScrollHorizontalOnly
|
||||
// ScrollVerticalOnly specifies the scrolling should only happen top to bottom.
|
||||
ScrollVerticalOnly = widget.ScrollVerticalOnly
|
||||
// ScrollNone turns off scrolling for this container.
|
||||
//
|
||||
// Since: 2.1
|
||||
ScrollNone = widget.ScrollNone
|
||||
)
|
||||
|
||||
// NewScroll creates a scrollable parent wrapping the specified content.
|
||||
// Note that this may cause the MinSize to be smaller than that of the passed object.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewScroll(content fyne.CanvasObject) *Scroll {
|
||||
return widget.NewScroll(content)
|
||||
}
|
||||
|
||||
// NewHScroll create a scrollable parent wrapping the specified content.
|
||||
// Note that this may cause the MinSize.Width to be smaller than that of the passed object.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewHScroll(content fyne.CanvasObject) *Scroll {
|
||||
return widget.NewHScroll(content)
|
||||
}
|
||||
|
||||
// NewVScroll a scrollable parent wrapping the specified content.
|
||||
// Note that this may cause the MinSize.Height to be smaller than that of the passed object.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewVScroll(content fyne.CanvasObject) *Scroll {
|
||||
return widget.NewVScroll(content)
|
||||
}
|
369
vendor/fyne.io/fyne/v2/container/split.go
generated
vendored
Normal file
369
vendor/fyne.io/fyne/v2/container/split.go
generated
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// Declare conformity with CanvasObject interface
|
||||
var _ fyne.CanvasObject = (*Split)(nil)
|
||||
|
||||
// Split defines a container whose size is split between two children.
|
||||
//
|
||||
// Since: 1.4
|
||||
type Split struct {
|
||||
widget.BaseWidget
|
||||
Offset float64
|
||||
Horizontal bool
|
||||
Leading fyne.CanvasObject
|
||||
Trailing fyne.CanvasObject
|
||||
}
|
||||
|
||||
// NewHSplit creates a horizontally arranged container with the specified leading and trailing elements.
|
||||
// A vertical split bar that can be dragged will be added between the elements.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewHSplit(leading, trailing fyne.CanvasObject) *Split {
|
||||
return newSplitContainer(true, leading, trailing)
|
||||
}
|
||||
|
||||
// NewVSplit creates a vertically arranged container with the specified top and bottom elements.
|
||||
// A horizontal split bar that can be dragged will be added between the elements.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewVSplit(top, bottom fyne.CanvasObject) *Split {
|
||||
return newSplitContainer(false, top, bottom)
|
||||
}
|
||||
|
||||
func newSplitContainer(horizontal bool, leading, trailing fyne.CanvasObject) *Split {
|
||||
s := &Split{
|
||||
Offset: 0.5, // Sensible default, can be overridden with SetOffset
|
||||
Horizontal: horizontal,
|
||||
Leading: leading,
|
||||
Trailing: trailing,
|
||||
}
|
||||
s.BaseWidget.ExtendBaseWidget(s)
|
||||
return s
|
||||
}
|
||||
|
||||
// CreateRenderer is a private method to Fyne which links this widget to its renderer
|
||||
func (s *Split) CreateRenderer() fyne.WidgetRenderer {
|
||||
s.BaseWidget.ExtendBaseWidget(s)
|
||||
d := newDivider(s)
|
||||
return &splitContainerRenderer{
|
||||
split: s,
|
||||
divider: d,
|
||||
objects: []fyne.CanvasObject{s.Leading, d, s.Trailing},
|
||||
}
|
||||
}
|
||||
|
||||
// ExtendBaseWidget is used by an extending widget to make use of BaseWidget functionality.
|
||||
//
|
||||
// Deprecated: Support for extending containers is being removed
|
||||
func (s *Split) ExtendBaseWidget(wid fyne.Widget) {
|
||||
s.BaseWidget.ExtendBaseWidget(wid)
|
||||
}
|
||||
|
||||
// SetOffset sets the offset (0.0 to 1.0) of the Split divider.
|
||||
// 0.0 - Leading is min size, Trailing uses all remaining space.
|
||||
// 0.5 - Leading & Trailing equally share the available space.
|
||||
// 1.0 - Trailing is min size, Leading uses all remaining space.
|
||||
func (s *Split) SetOffset(offset float64) {
|
||||
if s.Offset == offset {
|
||||
return
|
||||
}
|
||||
s.Offset = offset
|
||||
s.Refresh()
|
||||
}
|
||||
|
||||
var _ fyne.WidgetRenderer = (*splitContainerRenderer)(nil)
|
||||
|
||||
type splitContainerRenderer struct {
|
||||
split *Split
|
||||
divider *divider
|
||||
objects []fyne.CanvasObject
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Layout(size fyne.Size) {
|
||||
var dividerPos, leadingPos, trailingPos fyne.Position
|
||||
var dividerSize, leadingSize, trailingSize fyne.Size
|
||||
|
||||
if r.split.Horizontal {
|
||||
lw, tw := r.computeSplitLengths(size.Width, r.minLeadingWidth(), r.minTrailingWidth())
|
||||
leadingPos.X = 0
|
||||
leadingSize.Width = lw
|
||||
leadingSize.Height = size.Height
|
||||
dividerPos.X = lw
|
||||
dividerSize.Width = dividerThickness()
|
||||
dividerSize.Height = size.Height
|
||||
trailingPos.X = lw + dividerSize.Width
|
||||
trailingSize.Width = tw
|
||||
trailingSize.Height = size.Height
|
||||
} else {
|
||||
lh, th := r.computeSplitLengths(size.Height, r.minLeadingHeight(), r.minTrailingHeight())
|
||||
leadingPos.Y = 0
|
||||
leadingSize.Width = size.Width
|
||||
leadingSize.Height = lh
|
||||
dividerPos.Y = lh
|
||||
dividerSize.Width = size.Width
|
||||
dividerSize.Height = dividerThickness()
|
||||
trailingPos.Y = lh + dividerSize.Height
|
||||
trailingSize.Width = size.Width
|
||||
trailingSize.Height = th
|
||||
}
|
||||
|
||||
r.divider.Move(dividerPos)
|
||||
r.divider.Resize(dividerSize)
|
||||
r.split.Leading.Move(leadingPos)
|
||||
r.split.Leading.Resize(leadingSize)
|
||||
r.split.Trailing.Move(trailingPos)
|
||||
r.split.Trailing.Resize(trailingSize)
|
||||
canvas.Refresh(r.divider)
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) MinSize() fyne.Size {
|
||||
s := fyne.NewSize(0, 0)
|
||||
for _, o := range r.objects {
|
||||
min := o.MinSize()
|
||||
if r.split.Horizontal {
|
||||
s.Width += min.Width
|
||||
s.Height = fyne.Max(s.Height, min.Height)
|
||||
} else {
|
||||
s.Width = fyne.Max(s.Width, min.Width)
|
||||
s.Height += min.Height
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) Refresh() {
|
||||
r.objects[0] = r.split.Leading
|
||||
// [1] is divider which doesn't change
|
||||
r.objects[2] = r.split.Trailing
|
||||
r.Layout(r.split.Size())
|
||||
canvas.Refresh(r.split)
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) computeSplitLengths(total, lMin, tMin float32) (float32, float32) {
|
||||
available := float64(total - dividerThickness())
|
||||
if available <= 0 {
|
||||
return 0, 0
|
||||
}
|
||||
ld := float64(lMin)
|
||||
tr := float64(tMin)
|
||||
offset := r.split.Offset
|
||||
|
||||
min := ld / available
|
||||
max := 1 - tr/available
|
||||
if min <= max {
|
||||
if offset < min {
|
||||
offset = min
|
||||
}
|
||||
if offset > max {
|
||||
offset = max
|
||||
}
|
||||
} else {
|
||||
offset = ld / (ld + tr)
|
||||
}
|
||||
|
||||
ld = offset * available
|
||||
tr = available - ld
|
||||
return float32(ld), float32(tr)
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minLeadingWidth() float32 {
|
||||
if r.split.Leading.Visible() {
|
||||
return r.split.Leading.MinSize().Width
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minLeadingHeight() float32 {
|
||||
if r.split.Leading.Visible() {
|
||||
return r.split.Leading.MinSize().Height
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minTrailingWidth() float32 {
|
||||
if r.split.Trailing.Visible() {
|
||||
return r.split.Trailing.MinSize().Width
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *splitContainerRenderer) minTrailingHeight() float32 {
|
||||
if r.split.Trailing.Visible() {
|
||||
return r.split.Trailing.MinSize().Height
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Declare conformity with interfaces
|
||||
var _ fyne.CanvasObject = (*divider)(nil)
|
||||
var _ fyne.Draggable = (*divider)(nil)
|
||||
var _ desktop.Cursorable = (*divider)(nil)
|
||||
var _ desktop.Hoverable = (*divider)(nil)
|
||||
|
||||
type divider struct {
|
||||
widget.BaseWidget
|
||||
split *Split
|
||||
hovered bool
|
||||
startDragOff *fyne.Position
|
||||
currentDragPos fyne.Position
|
||||
}
|
||||
|
||||
func newDivider(split *Split) *divider {
|
||||
d := ÷r{
|
||||
split: split,
|
||||
}
|
||||
d.ExtendBaseWidget(d)
|
||||
return d
|
||||
}
|
||||
|
||||
// CreateRenderer is a private method to Fyne which links this widget to its renderer
|
||||
func (d *divider) CreateRenderer() fyne.WidgetRenderer {
|
||||
d.ExtendBaseWidget(d)
|
||||
background := canvas.NewRectangle(theme.ShadowColor())
|
||||
foreground := canvas.NewRectangle(theme.ForegroundColor())
|
||||
return ÷rRenderer{
|
||||
divider: d,
|
||||
background: background,
|
||||
foreground: foreground,
|
||||
objects: []fyne.CanvasObject{background, foreground},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *divider) Cursor() desktop.Cursor {
|
||||
if d.split.Horizontal {
|
||||
return desktop.HResizeCursor
|
||||
}
|
||||
return desktop.VResizeCursor
|
||||
}
|
||||
|
||||
func (d *divider) DragEnd() {
|
||||
d.startDragOff = nil
|
||||
}
|
||||
|
||||
func (d *divider) Dragged(e *fyne.DragEvent) {
|
||||
if d.startDragOff == nil {
|
||||
d.currentDragPos = d.Position().Add(e.Position)
|
||||
start := e.Position.Subtract(e.Dragged)
|
||||
d.startDragOff = &start
|
||||
} else {
|
||||
d.currentDragPos = d.currentDragPos.Add(e.Dragged)
|
||||
}
|
||||
|
||||
x, y := d.currentDragPos.Components()
|
||||
var offset, leadingRatio, trailingRatio float64
|
||||
if d.split.Horizontal {
|
||||
widthFree := float64(d.split.Size().Width - dividerThickness())
|
||||
leadingRatio = float64(d.split.Leading.MinSize().Width) / widthFree
|
||||
trailingRatio = 1. - (float64(d.split.Trailing.MinSize().Width) / widthFree)
|
||||
offset = float64(x-d.startDragOff.X) / widthFree
|
||||
} else {
|
||||
heightFree := float64(d.split.Size().Height - dividerThickness())
|
||||
leadingRatio = float64(d.split.Leading.MinSize().Height) / heightFree
|
||||
trailingRatio = 1. - (float64(d.split.Trailing.MinSize().Height) / heightFree)
|
||||
offset = float64(y-d.startDragOff.Y) / heightFree
|
||||
}
|
||||
|
||||
if offset < leadingRatio {
|
||||
offset = leadingRatio
|
||||
}
|
||||
if offset > trailingRatio {
|
||||
offset = trailingRatio
|
||||
}
|
||||
d.split.SetOffset(offset)
|
||||
}
|
||||
|
||||
func (d *divider) MouseIn(event *desktop.MouseEvent) {
|
||||
d.hovered = true
|
||||
d.split.Refresh()
|
||||
}
|
||||
|
||||
func (d *divider) MouseMoved(event *desktop.MouseEvent) {}
|
||||
|
||||
func (d *divider) MouseOut() {
|
||||
d.hovered = false
|
||||
d.split.Refresh()
|
||||
}
|
||||
|
||||
var _ fyne.WidgetRenderer = (*dividerRenderer)(nil)
|
||||
|
||||
type dividerRenderer struct {
|
||||
divider *divider
|
||||
background *canvas.Rectangle
|
||||
foreground *canvas.Rectangle
|
||||
objects []fyne.CanvasObject
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Layout(size fyne.Size) {
|
||||
r.background.Resize(size)
|
||||
var x, y, w, h float32
|
||||
if r.divider.split.Horizontal {
|
||||
x = (dividerThickness() - handleThickness()) / 2
|
||||
y = (size.Height - handleLength()) / 2
|
||||
w = handleThickness()
|
||||
h = handleLength()
|
||||
} else {
|
||||
x = (size.Width - handleLength()) / 2
|
||||
y = (dividerThickness() - handleThickness()) / 2
|
||||
w = handleLength()
|
||||
h = handleThickness()
|
||||
}
|
||||
r.foreground.Move(fyne.NewPos(x, y))
|
||||
r.foreground.Resize(fyne.NewSize(w, h))
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) MinSize() fyne.Size {
|
||||
if r.divider.split.Horizontal {
|
||||
return fyne.NewSize(dividerThickness(), dividerLength())
|
||||
}
|
||||
return fyne.NewSize(dividerLength(), dividerThickness())
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects
|
||||
}
|
||||
|
||||
func (r *dividerRenderer) Refresh() {
|
||||
if r.divider.hovered {
|
||||
r.background.FillColor = theme.HoverColor()
|
||||
} else {
|
||||
r.background.FillColor = theme.ShadowColor()
|
||||
}
|
||||
r.background.Refresh()
|
||||
r.foreground.FillColor = theme.ForegroundColor()
|
||||
r.foreground.Refresh()
|
||||
r.Layout(r.divider.Size())
|
||||
}
|
||||
|
||||
func dividerThickness() float32 {
|
||||
return theme.Padding() * 2
|
||||
}
|
||||
|
||||
func dividerLength() float32 {
|
||||
return theme.Padding() * 6
|
||||
}
|
||||
|
||||
func handleThickness() float32 {
|
||||
return theme.Padding() / 2
|
||||
}
|
||||
|
||||
func handleLength() float32 {
|
||||
return theme.Padding() * 4
|
||||
}
|
849
vendor/fyne.io/fyne/v2/container/tabs.go
generated
vendored
Normal file
849
vendor/fyne.io/fyne/v2/container/tabs.go
generated
vendored
Normal file
@ -0,0 +1,849 @@
|
||||
package container
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/driver/desktop"
|
||||
"fyne.io/fyne/v2/internal"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
// TabItem represents a single view in a tab view.
|
||||
// The Text and Icon are used for the tab button and the Content is shown when the corresponding tab is active.
|
||||
//
|
||||
// Since: 1.4
|
||||
type TabItem struct {
|
||||
Text string
|
||||
Icon fyne.Resource
|
||||
Content fyne.CanvasObject
|
||||
|
||||
button *tabButton
|
||||
}
|
||||
|
||||
// Disabled returns whether or not the TabItem is disabled.
|
||||
//
|
||||
// Since: 2.3
|
||||
func (ti *TabItem) Disabled() bool {
|
||||
if ti.button != nil {
|
||||
return ti.button.Disabled()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (ti *TabItem) disable() {
|
||||
if ti.button != nil {
|
||||
ti.button.Disable()
|
||||
}
|
||||
}
|
||||
|
||||
func (ti *TabItem) enable() {
|
||||
if ti.button != nil {
|
||||
ti.button.Enable()
|
||||
}
|
||||
}
|
||||
|
||||
// TabLocation is the location where the tabs of a tab container should be rendered
|
||||
//
|
||||
// Since: 1.4
|
||||
type TabLocation int
|
||||
|
||||
// TabLocation values
|
||||
const (
|
||||
TabLocationTop TabLocation = iota
|
||||
TabLocationLeading
|
||||
TabLocationBottom
|
||||
TabLocationTrailing
|
||||
)
|
||||
|
||||
// NewTabItem creates a new item for a tabbed widget - each item specifies the content and a label for its tab.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewTabItem(text string, content fyne.CanvasObject) *TabItem {
|
||||
return &TabItem{Text: text, Content: content}
|
||||
}
|
||||
|
||||
// NewTabItemWithIcon creates a new item for a tabbed widget - each item specifies the content and a label with an icon for its tab.
|
||||
//
|
||||
// Since: 1.4
|
||||
func NewTabItemWithIcon(text string, icon fyne.Resource, content fyne.CanvasObject) *TabItem {
|
||||
return &TabItem{Text: text, Icon: icon, Content: content}
|
||||
}
|
||||
|
||||
type baseTabs interface {
|
||||
onUnselected() func(*TabItem)
|
||||
onSelected() func(*TabItem)
|
||||
|
||||
items() []*TabItem
|
||||
setItems([]*TabItem)
|
||||
|
||||
selected() int
|
||||
setSelected(int)
|
||||
|
||||
tabLocation() TabLocation
|
||||
|
||||
transitioning() bool
|
||||
setTransitioning(bool)
|
||||
}
|
||||
|
||||
func tabsAdjustedLocation(l TabLocation) TabLocation {
|
||||
// Mobile has limited screen space, so don't put app tab bar on long edges
|
||||
if d := fyne.CurrentDevice(); d.IsMobile() {
|
||||
if o := d.Orientation(); fyne.IsVertical(o) {
|
||||
if l == TabLocationLeading {
|
||||
return TabLocationTop
|
||||
} else if l == TabLocationTrailing {
|
||||
return TabLocationBottom
|
||||
}
|
||||
} else {
|
||||
if l == TabLocationTop {
|
||||
return TabLocationLeading
|
||||
} else if l == TabLocationBottom {
|
||||
return TabLocationTrailing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
func buildPopUpMenu(t baseTabs, button *widget.Button, items []*fyne.MenuItem) *widget.PopUpMenu {
|
||||
d := fyne.CurrentApp().Driver()
|
||||
c := d.CanvasForObject(button)
|
||||
popUpMenu := widget.NewPopUpMenu(fyne.NewMenu("", items...), c)
|
||||
buttonPos := d.AbsolutePositionForObject(button)
|
||||
buttonSize := button.Size()
|
||||
popUpMin := popUpMenu.MinSize()
|
||||
var popUpPos fyne.Position
|
||||
switch t.tabLocation() {
|
||||
case TabLocationLeading:
|
||||
popUpPos.X = buttonPos.X + buttonSize.Width
|
||||
popUpPos.Y = buttonPos.Y + buttonSize.Height - popUpMin.Height
|
||||
case TabLocationTrailing:
|
||||
popUpPos.X = buttonPos.X - popUpMin.Width
|
||||
popUpPos.Y = buttonPos.Y + buttonSize.Height - popUpMin.Height
|
||||
case TabLocationTop:
|
||||
popUpPos.X = buttonPos.X + buttonSize.Width - popUpMin.Width
|
||||
popUpPos.Y = buttonPos.Y + buttonSize.Height
|
||||
case TabLocationBottom:
|
||||
popUpPos.X = buttonPos.X + buttonSize.Width - popUpMin.Width
|
||||
popUpPos.Y = buttonPos.Y - popUpMin.Height
|
||||
}
|
||||
if popUpPos.X < 0 {
|
||||
popUpPos.X = 0
|
||||
}
|
||||
if popUpPos.Y < 0 {
|
||||
popUpPos.Y = 0
|
||||
}
|
||||
popUpMenu.ShowAtPosition(popUpPos)
|
||||
return popUpMenu
|
||||
}
|
||||
|
||||
func removeIndex(t baseTabs, index int) {
|
||||
items := t.items()
|
||||
if index < 0 || index >= len(items) {
|
||||
return
|
||||
}
|
||||
setItems(t, append(items[:index], items[index+1:]...))
|
||||
if s := t.selected(); index < s {
|
||||
t.setSelected(s - 1)
|
||||
}
|
||||
}
|
||||
|
||||
func removeItem(t baseTabs, item *TabItem) {
|
||||
for index, existingItem := range t.items() {
|
||||
if existingItem == item {
|
||||
removeIndex(t, index)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func selected(t baseTabs) *TabItem {
|
||||
selected := t.selected()
|
||||
items := t.items()
|
||||
if selected < 0 || selected >= len(items) {
|
||||
return nil
|
||||
}
|
||||
return items[selected]
|
||||
}
|
||||
|
||||
func selectIndex(t baseTabs, index int) {
|
||||
selected := t.selected()
|
||||
|
||||
if selected == index {
|
||||
// No change, so do nothing
|
||||
return
|
||||
}
|
||||
|
||||
items := t.items()
|
||||
|
||||
if f := t.onUnselected(); f != nil && selected >= 0 && selected < len(items) {
|
||||
// Notification of unselected
|
||||
f(items[selected])
|
||||
}
|
||||
|
||||
if index < 0 || index >= len(items) {
|
||||
// Out of bounds, so do nothing
|
||||
return
|
||||
}
|
||||
|
||||
t.setTransitioning(true)
|
||||
t.setSelected(index)
|
||||
|
||||
if f := t.onSelected(); f != nil {
|
||||
// Notification of selected
|
||||
f(items[index])
|
||||
}
|
||||
}
|
||||
|
||||
func selectItem(t baseTabs, item *TabItem) {
|
||||
for i, child := range t.items() {
|
||||
if child == item {
|
||||
selectIndex(t, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setItems(t baseTabs, items []*TabItem) {
|
||||
if internal.HintsEnabled && mismatchedTabItems(items) {
|
||||
internal.LogHint("Tab items should all have the same type of content (text, icons or both)")
|
||||
}
|
||||
t.setItems(items)
|
||||
selected := t.selected()
|
||||
count := len(items)
|
||||
switch {
|
||||
case count == 0:
|
||||
// No items available to be selected
|
||||
selectIndex(t, -1) // Unsure OnUnselected gets called if applicable
|
||||
t.setSelected(-1)
|
||||
case selected < 0:
|
||||
// Current is first tab item
|
||||
selectIndex(t, 0)
|
||||
case selected >= count:
|
||||
// Current doesn't exist, select last tab
|
||||
selectIndex(t, count-1)
|
||||
}
|
||||
}
|
||||
|
||||
func disableIndex(t baseTabs, index int) {
|
||||
items := t.items()
|
||||
if index < 0 || index >= len(items) {
|
||||
return
|
||||
}
|
||||
|
||||
item := items[index]
|
||||
item.disable()
|
||||
|
||||
if selected(t) == item {
|
||||
// the disabled tab is currently selected, so select the first enabled tab
|
||||
for i, it := range items {
|
||||
if !it.Disabled() {
|
||||
selectIndex(t, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if selected(t) == item {
|
||||
selectIndex(t, -1) // no other tab is able to be selected
|
||||
}
|
||||
}
|
||||
|
||||
func disableItem(t baseTabs, item *TabItem) {
|
||||
for i, it := range t.items() {
|
||||
if it == item {
|
||||
disableIndex(t, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enableIndex(t baseTabs, index int) {
|
||||
items := t.items()
|
||||
if index < 0 || index >= len(items) {
|
||||
return
|
||||
}
|
||||
|
||||
item := items[index]
|
||||
item.enable()
|
||||
}
|
||||
|
||||
func enableItem(t baseTabs, item *TabItem) {
|
||||
for i, it := range t.items() {
|
||||
if it == item {
|
||||
enableIndex(t, i)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type baseTabsRenderer struct {
|
||||
positionAnimation, sizeAnimation *fyne.Animation
|
||||
|
||||
lastIndicatorMutex sync.RWMutex
|
||||
lastIndicatorPos fyne.Position
|
||||
lastIndicatorSize fyne.Size
|
||||
lastIndicatorHidden bool
|
||||
|
||||
action *widget.Button
|
||||
bar *fyne.Container
|
||||
divider, indicator *canvas.Rectangle
|
||||
|
||||
tabs baseTabs
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) applyTheme(t baseTabs) {
|
||||
if r.action != nil {
|
||||
r.action.SetIcon(moreIcon(t))
|
||||
}
|
||||
r.divider.FillColor = theme.ShadowColor()
|
||||
r.indicator.FillColor = theme.PrimaryColor()
|
||||
r.indicator.CornerRadius = theme.SelectionRadiusSize()
|
||||
|
||||
for _, tab := range r.tabs.items() {
|
||||
tab.Content.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) layout(t baseTabs, size fyne.Size) {
|
||||
var (
|
||||
barPos, dividerPos, contentPos fyne.Position
|
||||
barSize, dividerSize, contentSize fyne.Size
|
||||
)
|
||||
|
||||
barMin := r.bar.MinSize()
|
||||
|
||||
padding := theme.Padding()
|
||||
switch t.tabLocation() {
|
||||
case TabLocationTop:
|
||||
barHeight := barMin.Height
|
||||
barPos = fyne.NewPos(0, 0)
|
||||
barSize = fyne.NewSize(size.Width, barHeight)
|
||||
dividerPos = fyne.NewPos(0, barHeight)
|
||||
dividerSize = fyne.NewSize(size.Width, padding)
|
||||
contentPos = fyne.NewPos(0, barHeight+padding)
|
||||
contentSize = fyne.NewSize(size.Width, size.Height-barHeight-padding)
|
||||
case TabLocationLeading:
|
||||
barWidth := barMin.Width
|
||||
barPos = fyne.NewPos(0, 0)
|
||||
barSize = fyne.NewSize(barWidth, size.Height)
|
||||
dividerPos = fyne.NewPos(barWidth, 0)
|
||||
dividerSize = fyne.NewSize(padding, size.Height)
|
||||
contentPos = fyne.NewPos(barWidth+theme.Padding(), 0)
|
||||
contentSize = fyne.NewSize(size.Width-barWidth-padding, size.Height)
|
||||
case TabLocationBottom:
|
||||
barHeight := barMin.Height
|
||||
barPos = fyne.NewPos(0, size.Height-barHeight)
|
||||
barSize = fyne.NewSize(size.Width, barHeight)
|
||||
dividerPos = fyne.NewPos(0, size.Height-barHeight-padding)
|
||||
dividerSize = fyne.NewSize(size.Width, padding)
|
||||
contentPos = fyne.NewPos(0, 0)
|
||||
contentSize = fyne.NewSize(size.Width, size.Height-barHeight-padding)
|
||||
case TabLocationTrailing:
|
||||
barWidth := barMin.Width
|
||||
barPos = fyne.NewPos(size.Width-barWidth, 0)
|
||||
barSize = fyne.NewSize(barWidth, size.Height)
|
||||
dividerPos = fyne.NewPos(size.Width-barWidth-padding, 0)
|
||||
dividerSize = fyne.NewSize(padding, size.Height)
|
||||
contentPos = fyne.NewPos(0, 0)
|
||||
contentSize = fyne.NewSize(size.Width-barWidth-padding, size.Height)
|
||||
}
|
||||
|
||||
r.bar.Move(barPos)
|
||||
r.bar.Resize(barSize)
|
||||
r.divider.Move(dividerPos)
|
||||
r.divider.Resize(dividerSize)
|
||||
selected := t.selected()
|
||||
for i, ti := range t.items() {
|
||||
if i == selected {
|
||||
ti.Content.Move(contentPos)
|
||||
ti.Content.Resize(contentSize)
|
||||
ti.Content.Show()
|
||||
} else {
|
||||
ti.Content.Hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) minSize(t baseTabs) fyne.Size {
|
||||
pad := theme.Padding()
|
||||
buttonPad := pad
|
||||
barMin := r.bar.MinSize()
|
||||
tabsMin := r.bar.Objects[0].MinSize()
|
||||
accessory := r.bar.Objects[1]
|
||||
accessoryMin := accessory.MinSize()
|
||||
if scroll, ok := r.bar.Objects[0].(*Scroll); ok && len(scroll.Content.(*fyne.Container).Objects) == 0 {
|
||||
tabsMin = fyne.Size{} // scroller forces 32 where we don't need any space
|
||||
buttonPad = 0
|
||||
} else if group, ok := r.bar.Objects[0].(*fyne.Container); ok && len(group.Objects) > 0 {
|
||||
tabsMin = group.Objects[0].MinSize()
|
||||
buttonPad = 0
|
||||
}
|
||||
if !accessory.Visible() || accessoryMin.Width == 0 {
|
||||
buttonPad = 0
|
||||
accessoryMin = fyne.Size{}
|
||||
}
|
||||
|
||||
contentMin := fyne.NewSize(0, 0)
|
||||
for _, content := range t.items() {
|
||||
contentMin = contentMin.Max(content.Content.MinSize())
|
||||
}
|
||||
|
||||
switch t.tabLocation() {
|
||||
case TabLocationLeading, TabLocationTrailing:
|
||||
return fyne.NewSize(barMin.Width+contentMin.Width+pad,
|
||||
fyne.Max(contentMin.Height, accessoryMin.Height+buttonPad+tabsMin.Height))
|
||||
default:
|
||||
return fyne.NewSize(fyne.Max(contentMin.Width, accessoryMin.Width+buttonPad+tabsMin.Width),
|
||||
barMin.Height+contentMin.Height+pad)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) moveIndicator(pos fyne.Position, siz fyne.Size, animate bool) {
|
||||
r.lastIndicatorMutex.RLock()
|
||||
isSameState := r.lastIndicatorPos.Subtract(pos).IsZero() && r.lastIndicatorSize.Subtract(siz).IsZero() &&
|
||||
r.lastIndicatorHidden == r.indicator.Hidden
|
||||
r.lastIndicatorMutex.RUnlock()
|
||||
if isSameState {
|
||||
return
|
||||
}
|
||||
|
||||
if r.positionAnimation != nil {
|
||||
r.positionAnimation.Stop()
|
||||
r.positionAnimation = nil
|
||||
}
|
||||
if r.sizeAnimation != nil {
|
||||
r.sizeAnimation.Stop()
|
||||
r.sizeAnimation = nil
|
||||
}
|
||||
|
||||
r.indicator.FillColor = theme.PrimaryColor()
|
||||
if r.indicator.Position().IsZero() {
|
||||
r.indicator.Move(pos)
|
||||
r.indicator.Resize(siz)
|
||||
r.indicator.Refresh()
|
||||
return
|
||||
}
|
||||
|
||||
r.lastIndicatorMutex.Lock()
|
||||
r.lastIndicatorPos = pos
|
||||
r.lastIndicatorSize = siz
|
||||
r.lastIndicatorHidden = r.indicator.Hidden
|
||||
r.lastIndicatorMutex.Unlock()
|
||||
|
||||
if animate && fyne.CurrentApp().Settings().ShowAnimations() {
|
||||
r.positionAnimation = canvas.NewPositionAnimation(r.indicator.Position(), pos, canvas.DurationShort, func(p fyne.Position) {
|
||||
r.indicator.Move(p)
|
||||
r.indicator.Refresh()
|
||||
if pos == p {
|
||||
r.positionAnimation.Stop()
|
||||
r.positionAnimation = nil
|
||||
}
|
||||
})
|
||||
r.sizeAnimation = canvas.NewSizeAnimation(r.indicator.Size(), siz, canvas.DurationShort, func(s fyne.Size) {
|
||||
r.indicator.Resize(s)
|
||||
r.indicator.Refresh()
|
||||
if siz == s {
|
||||
r.sizeAnimation.Stop()
|
||||
r.sizeAnimation = nil
|
||||
}
|
||||
})
|
||||
|
||||
r.positionAnimation.Start()
|
||||
r.sizeAnimation.Start()
|
||||
} else {
|
||||
r.indicator.Move(pos)
|
||||
r.indicator.Resize(siz)
|
||||
r.indicator.Refresh()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) objects(t baseTabs) []fyne.CanvasObject {
|
||||
objects := []fyne.CanvasObject{r.bar, r.divider, r.indicator}
|
||||
if i, is := t.selected(), t.items(); i >= 0 && i < len(is) {
|
||||
objects = append(objects, is[i].Content)
|
||||
}
|
||||
return objects
|
||||
}
|
||||
|
||||
func (r *baseTabsRenderer) refresh(t baseTabs) {
|
||||
r.applyTheme(t)
|
||||
|
||||
r.bar.Refresh()
|
||||
r.divider.Refresh()
|
||||
r.indicator.Refresh()
|
||||
}
|
||||
|
||||
type buttonIconPosition int
|
||||
|
||||
const (
|
||||
buttonIconInline buttonIconPosition = iota
|
||||
buttonIconTop
|
||||
)
|
||||
|
||||
var _ fyne.Widget = (*tabButton)(nil)
|
||||
var _ fyne.Tappable = (*tabButton)(nil)
|
||||
var _ desktop.Hoverable = (*tabButton)(nil)
|
||||
|
||||
type tabButton struct {
|
||||
widget.DisableableWidget
|
||||
hovered bool
|
||||
icon fyne.Resource
|
||||
iconPosition buttonIconPosition
|
||||
importance widget.Importance
|
||||
onTapped func()
|
||||
onClosed func()
|
||||
text string
|
||||
textAlignment fyne.TextAlign
|
||||
}
|
||||
|
||||
func (b *tabButton) CreateRenderer() fyne.WidgetRenderer {
|
||||
b.ExtendBaseWidget(b)
|
||||
background := canvas.NewRectangle(theme.HoverColor())
|
||||
background.CornerRadius = theme.SelectionRadiusSize()
|
||||
background.Hide()
|
||||
icon := canvas.NewImageFromResource(b.icon)
|
||||
if b.icon == nil {
|
||||
icon.Hide()
|
||||
}
|
||||
|
||||
label := canvas.NewText(b.text, theme.ForegroundColor())
|
||||
label.TextStyle.Bold = true
|
||||
|
||||
close := &tabCloseButton{
|
||||
parent: b,
|
||||
onTapped: func() {
|
||||
if f := b.onClosed; f != nil {
|
||||
f()
|
||||
}
|
||||
},
|
||||
}
|
||||
close.ExtendBaseWidget(close)
|
||||
close.Hide()
|
||||
|
||||
objects := []fyne.CanvasObject{background, label, close, icon}
|
||||
r := &tabButtonRenderer{
|
||||
button: b,
|
||||
background: background,
|
||||
icon: icon,
|
||||
label: label,
|
||||
close: close,
|
||||
objects: objects,
|
||||
}
|
||||
r.Refresh()
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *tabButton) MinSize() fyne.Size {
|
||||
b.ExtendBaseWidget(b)
|
||||
return b.BaseWidget.MinSize()
|
||||
}
|
||||
|
||||
func (b *tabButton) MouseIn(*desktop.MouseEvent) {
|
||||
b.hovered = true
|
||||
b.Refresh()
|
||||
}
|
||||
|
||||
func (b *tabButton) MouseMoved(*desktop.MouseEvent) {
|
||||
}
|
||||
|
||||
func (b *tabButton) MouseOut() {
|
||||
b.hovered = false
|
||||
b.Refresh()
|
||||
}
|
||||
|
||||
func (b *tabButton) Tapped(*fyne.PointEvent) {
|
||||
if b.Disabled() {
|
||||
return
|
||||
}
|
||||
|
||||
b.onTapped()
|
||||
}
|
||||
|
||||
type tabButtonRenderer struct {
|
||||
button *tabButton
|
||||
background *canvas.Rectangle
|
||||
icon *canvas.Image
|
||||
label *canvas.Text
|
||||
close *tabCloseButton
|
||||
objects []fyne.CanvasObject
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) Layout(size fyne.Size) {
|
||||
r.background.Resize(size)
|
||||
padding := r.padding()
|
||||
innerSize := size.Subtract(padding)
|
||||
innerOffset := fyne.NewPos(padding.Width/2, padding.Height/2)
|
||||
labelShift := float32(0)
|
||||
if r.icon.Visible() {
|
||||
iconSize := r.iconSize()
|
||||
var iconOffset fyne.Position
|
||||
if r.button.iconPosition == buttonIconTop {
|
||||
iconOffset = fyne.NewPos((innerSize.Width-iconSize)/2, 0)
|
||||
} else {
|
||||
iconOffset = fyne.NewPos(0, (innerSize.Height-iconSize)/2)
|
||||
}
|
||||
r.icon.Resize(fyne.NewSquareSize(iconSize))
|
||||
r.icon.Move(innerOffset.Add(iconOffset))
|
||||
labelShift = iconSize + theme.Padding()
|
||||
}
|
||||
if r.label.Text != "" {
|
||||
var labelOffset fyne.Position
|
||||
var labelSize fyne.Size
|
||||
if r.button.iconPosition == buttonIconTop {
|
||||
labelOffset = fyne.NewPos(0, labelShift)
|
||||
labelSize = fyne.NewSize(innerSize.Width, r.label.MinSize().Height)
|
||||
} else {
|
||||
labelOffset = fyne.NewPos(labelShift, 0)
|
||||
labelSize = fyne.NewSize(innerSize.Width-labelShift, innerSize.Height)
|
||||
}
|
||||
r.label.Resize(labelSize)
|
||||
r.label.Move(innerOffset.Add(labelOffset))
|
||||
}
|
||||
inlineIconSize := theme.IconInlineSize()
|
||||
r.close.Move(fyne.NewPos(size.Width-inlineIconSize-theme.Padding(), (size.Height-inlineIconSize)/2))
|
||||
r.close.Resize(fyne.NewSquareSize(inlineIconSize))
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) MinSize() fyne.Size {
|
||||
var contentWidth, contentHeight float32
|
||||
textSize := r.label.MinSize()
|
||||
iconSize := r.iconSize()
|
||||
padding := theme.Padding()
|
||||
if r.button.iconPosition == buttonIconTop {
|
||||
contentWidth = fyne.Max(textSize.Width, iconSize)
|
||||
if r.icon.Visible() {
|
||||
contentHeight += iconSize
|
||||
}
|
||||
if r.label.Text != "" {
|
||||
if r.icon.Visible() {
|
||||
contentHeight += padding
|
||||
}
|
||||
contentHeight += textSize.Height
|
||||
}
|
||||
} else {
|
||||
contentHeight = fyne.Max(textSize.Height, iconSize)
|
||||
if r.icon.Visible() {
|
||||
contentWidth += iconSize
|
||||
}
|
||||
if r.label.Text != "" {
|
||||
if r.icon.Visible() {
|
||||
contentWidth += padding
|
||||
}
|
||||
contentWidth += textSize.Width
|
||||
}
|
||||
}
|
||||
if r.button.onClosed != nil {
|
||||
inlineIconSize := theme.IconInlineSize()
|
||||
contentWidth += inlineIconSize + padding
|
||||
contentHeight = fyne.Max(contentHeight, inlineIconSize)
|
||||
}
|
||||
return fyne.NewSize(contentWidth, contentHeight).Add(r.padding())
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) Refresh() {
|
||||
if r.button.hovered && !r.button.Disabled() {
|
||||
r.background.FillColor = theme.HoverColor()
|
||||
r.background.CornerRadius = theme.SelectionRadiusSize()
|
||||
r.background.Show()
|
||||
} else {
|
||||
r.background.Hide()
|
||||
}
|
||||
r.background.Refresh()
|
||||
|
||||
r.label.Text = r.button.text
|
||||
r.label.Alignment = r.button.textAlignment
|
||||
if !r.button.Disabled() {
|
||||
if r.button.importance == widget.HighImportance {
|
||||
r.label.Color = theme.PrimaryColor()
|
||||
} else {
|
||||
r.label.Color = theme.ForegroundColor()
|
||||
}
|
||||
} else {
|
||||
r.label.Color = theme.DisabledColor()
|
||||
}
|
||||
r.label.TextSize = theme.TextSize()
|
||||
if r.button.text == "" {
|
||||
r.label.Hide()
|
||||
} else {
|
||||
r.label.Show()
|
||||
}
|
||||
|
||||
r.icon.Resource = r.button.icon
|
||||
if r.icon.Resource != nil {
|
||||
r.icon.Show()
|
||||
switch res := r.icon.Resource.(type) {
|
||||
case *theme.ThemedResource:
|
||||
if r.button.importance == widget.HighImportance {
|
||||
r.icon.Resource = theme.NewPrimaryThemedResource(res)
|
||||
r.icon.Refresh()
|
||||
}
|
||||
case *theme.PrimaryThemedResource:
|
||||
if r.button.importance != widget.HighImportance {
|
||||
r.icon.Resource = res.Original()
|
||||
r.icon.Refresh()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
r.icon.Hide()
|
||||
}
|
||||
|
||||
if d := fyne.CurrentDevice(); r.button.onClosed != nil && (d.IsMobile() || r.button.hovered || r.close.hovered) {
|
||||
r.close.Show()
|
||||
} else {
|
||||
r.close.Hide()
|
||||
}
|
||||
r.close.Refresh()
|
||||
|
||||
canvas.Refresh(r.button)
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) iconSize() float32 {
|
||||
if r.button.iconPosition == buttonIconTop {
|
||||
return 2 * theme.IconInlineSize()
|
||||
}
|
||||
|
||||
return theme.IconInlineSize()
|
||||
}
|
||||
|
||||
func (r *tabButtonRenderer) padding() fyne.Size {
|
||||
padding := theme.InnerPadding()
|
||||
if r.label.Text != "" && r.button.iconPosition == buttonIconInline {
|
||||
return fyne.NewSquareSize(padding * 2)
|
||||
}
|
||||
return fyne.NewSize(padding, padding*2)
|
||||
}
|
||||
|
||||
var _ fyne.Widget = (*tabCloseButton)(nil)
|
||||
var _ fyne.Tappable = (*tabCloseButton)(nil)
|
||||
var _ desktop.Hoverable = (*tabCloseButton)(nil)
|
||||
|
||||
type tabCloseButton struct {
|
||||
widget.BaseWidget
|
||||
parent *tabButton
|
||||
hovered bool
|
||||
onTapped func()
|
||||
}
|
||||
|
||||
func (b *tabCloseButton) CreateRenderer() fyne.WidgetRenderer {
|
||||
b.ExtendBaseWidget(b)
|
||||
background := canvas.NewRectangle(theme.HoverColor())
|
||||
background.CornerRadius = theme.SelectionRadiusSize()
|
||||
background.Hide()
|
||||
icon := canvas.NewImageFromResource(theme.CancelIcon())
|
||||
|
||||
r := &tabCloseButtonRenderer{
|
||||
button: b,
|
||||
background: background,
|
||||
icon: icon,
|
||||
objects: []fyne.CanvasObject{background, icon},
|
||||
}
|
||||
r.Refresh()
|
||||
return r
|
||||
}
|
||||
|
||||
func (b *tabCloseButton) MinSize() fyne.Size {
|
||||
b.ExtendBaseWidget(b)
|
||||
return b.BaseWidget.MinSize()
|
||||
}
|
||||
|
||||
func (b *tabCloseButton) MouseIn(*desktop.MouseEvent) {
|
||||
b.hovered = true
|
||||
b.parent.Refresh()
|
||||
}
|
||||
|
||||
func (b *tabCloseButton) MouseMoved(*desktop.MouseEvent) {
|
||||
}
|
||||
|
||||
func (b *tabCloseButton) MouseOut() {
|
||||
b.hovered = false
|
||||
b.parent.Refresh()
|
||||
}
|
||||
|
||||
func (b *tabCloseButton) Tapped(*fyne.PointEvent) {
|
||||
b.onTapped()
|
||||
}
|
||||
|
||||
type tabCloseButtonRenderer struct {
|
||||
button *tabCloseButton
|
||||
background *canvas.Rectangle
|
||||
icon *canvas.Image
|
||||
objects []fyne.CanvasObject
|
||||
}
|
||||
|
||||
func (r *tabCloseButtonRenderer) Destroy() {
|
||||
}
|
||||
|
||||
func (r *tabCloseButtonRenderer) Layout(size fyne.Size) {
|
||||
r.background.Resize(size)
|
||||
r.icon.Resize(size)
|
||||
}
|
||||
|
||||
func (r *tabCloseButtonRenderer) MinSize() fyne.Size {
|
||||
return fyne.NewSquareSize(theme.IconInlineSize())
|
||||
}
|
||||
|
||||
func (r *tabCloseButtonRenderer) Objects() []fyne.CanvasObject {
|
||||
return r.objects
|
||||
}
|
||||
|
||||
func (r *tabCloseButtonRenderer) Refresh() {
|
||||
if r.button.hovered {
|
||||
r.background.FillColor = theme.HoverColor()
|
||||
r.background.CornerRadius = theme.SelectionRadiusSize()
|
||||
r.background.Show()
|
||||
} else {
|
||||
r.background.Hide()
|
||||
}
|
||||
r.background.Refresh()
|
||||
switch res := r.icon.Resource.(type) {
|
||||
case *theme.ThemedResource:
|
||||
if r.button.parent.importance == widget.HighImportance {
|
||||
r.icon.Resource = theme.NewPrimaryThemedResource(res)
|
||||
}
|
||||
case *theme.PrimaryThemedResource:
|
||||
if r.button.parent.importance != widget.HighImportance {
|
||||
r.icon.Resource = res.Original()
|
||||
}
|
||||
}
|
||||
r.icon.Refresh()
|
||||
}
|
||||
|
||||
func mismatchedTabItems(items []*TabItem) bool {
|
||||
var hasText, hasIcon bool
|
||||
for _, tab := range items {
|
||||
hasText = hasText || tab.Text != ""
|
||||
hasIcon = hasIcon || tab.Icon != nil
|
||||
}
|
||||
|
||||
mismatch := false
|
||||
for _, tab := range items {
|
||||
if (hasText && tab.Text == "") || (hasIcon && tab.Icon == nil) {
|
||||
mismatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return mismatch
|
||||
}
|
||||
|
||||
func moreIcon(t baseTabs) fyne.Resource {
|
||||
if l := t.tabLocation(); l == TabLocationLeading || l == TabLocationTrailing {
|
||||
return theme.MoreVerticalIcon()
|
||||
}
|
||||
return theme.MoreHorizontalIcon()
|
||||
}
|
178
vendor/fyne.io/fyne/v2/data/binding/binding.go
generated
vendored
Normal file
178
vendor/fyne.io/fyne/v2/data/binding/binding.go
generated
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
//go:generate go run gen.go
|
||||
|
||||
// Package binding provides support for binding data to widgets.
|
||||
package binding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
errKeyNotFound = errors.New("key not found")
|
||||
errOutOfBounds = errors.New("index out of bounds")
|
||||
errParseFailed = errors.New("format did not match 1 value")
|
||||
|
||||
// As an optimisation we connect any listeners asking for the same key, so that there is only 1 per preference item.
|
||||
prefBinds = newPreferencesMap()
|
||||
)
|
||||
|
||||
// DataItem is the base interface for all bindable data items.
|
||||
//
|
||||
// Since: 2.0
|
||||
type DataItem interface {
|
||||
// AddListener attaches a new change listener to this DataItem.
|
||||
// Listeners are called each time the data inside this DataItem changes.
|
||||
// Additionally the listener will be triggered upon successful connection to get the current value.
|
||||
AddListener(DataListener)
|
||||
// RemoveListener will detach the specified change listener from the DataItem.
|
||||
// Disconnected listener will no longer be triggered when changes occur.
|
||||
RemoveListener(DataListener)
|
||||
}
|
||||
|
||||
// DataListener is any object that can register for changes in a bindable DataItem.
|
||||
// See NewDataListener to define a new listener using just an inline function.
|
||||
//
|
||||
// Since: 2.0
|
||||
type DataListener interface {
|
||||
DataChanged()
|
||||
}
|
||||
|
||||
// NewDataListener is a helper function that creates a new listener type from a simple callback function.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewDataListener(fn func()) DataListener {
|
||||
return &listener{fn}
|
||||
}
|
||||
|
||||
type listener struct {
|
||||
callback func()
|
||||
}
|
||||
|
||||
func (l *listener) DataChanged() {
|
||||
l.callback()
|
||||
}
|
||||
|
||||
type base struct {
|
||||
listeners sync.Map // map[DataListener]bool
|
||||
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// AddListener allows a data listener to be informed of changes to this item.
|
||||
func (b *base) AddListener(l DataListener) {
|
||||
b.listeners.Store(l, true)
|
||||
queueItem(l.DataChanged)
|
||||
}
|
||||
|
||||
// RemoveListener should be called if the listener is no longer interested in being informed of data change events.
|
||||
func (b *base) RemoveListener(l DataListener) {
|
||||
b.listeners.Delete(l)
|
||||
}
|
||||
|
||||
func (b *base) trigger() {
|
||||
b.listeners.Range(func(key, _ interface{}) bool {
|
||||
queueItem(key.(DataListener).DataChanged)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// Untyped supports binding a interface{} value.
|
||||
//
|
||||
// Since: 2.1
|
||||
type Untyped interface {
|
||||
DataItem
|
||||
Get() (interface{}, error)
|
||||
Set(interface{}) error
|
||||
}
|
||||
|
||||
// NewUntyped returns a bindable interface{} value that is managed internally.
|
||||
//
|
||||
// Since: 2.1
|
||||
func NewUntyped() Untyped {
|
||||
var blank interface{} = nil
|
||||
v := &blank
|
||||
return &boundUntyped{val: reflect.ValueOf(v).Elem()}
|
||||
}
|
||||
|
||||
type boundUntyped struct {
|
||||
base
|
||||
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func (b *boundUntyped) Get() (interface{}, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
return b.val.Interface(), nil
|
||||
}
|
||||
|
||||
func (b *boundUntyped) Set(val interface{}) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.val.Interface() == val {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.val.Set(reflect.ValueOf(val))
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExternalUntyped supports binding a interface{} value to an external value.
|
||||
//
|
||||
// Since: 2.1
|
||||
type ExternalUntyped interface {
|
||||
Untyped
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// BindUntyped returns a bindable interface{} value that is bound to an external type.
|
||||
// The parameter must be a pointer to the type you wish to bind.
|
||||
//
|
||||
// Since: 2.1
|
||||
func BindUntyped(v interface{}) ExternalUntyped {
|
||||
t := reflect.TypeOf(v)
|
||||
if t.Kind() != reflect.Ptr {
|
||||
fyne.LogError("Invalid type passed to BindUntyped, must be a pointer", nil)
|
||||
v = nil
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
var blank interface{}
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
|
||||
b := &boundExternalUntyped{}
|
||||
b.val = reflect.ValueOf(v).Elem()
|
||||
b.old = b.val.Interface()
|
||||
return b
|
||||
}
|
||||
|
||||
type boundExternalUntyped struct {
|
||||
boundUntyped
|
||||
|
||||
old interface{}
|
||||
}
|
||||
|
||||
func (b *boundExternalUntyped) Set(val interface{}) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.old == val {
|
||||
return nil
|
||||
}
|
||||
b.val.Set(reflect.ValueOf(val))
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalUntyped) Reload() error {
|
||||
return b.Set(b.val.Interface())
|
||||
}
|
647
vendor/fyne.io/fyne/v2/data/binding/binditems.go
generated
vendored
Normal file
647
vendor/fyne.io/fyne/v2/data/binding/binditems.go
generated
vendored
Normal file
@ -0,0 +1,647 @@
|
||||
// auto-generated
|
||||
// **** THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT IT **** //
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Bool supports binding a bool value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Bool interface {
|
||||
DataItem
|
||||
Get() (bool, error)
|
||||
Set(bool) error
|
||||
}
|
||||
|
||||
// ExternalBool supports binding a bool value to an external value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type ExternalBool interface {
|
||||
Bool
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewBool returns a bindable bool value that is managed internally.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewBool() Bool {
|
||||
var blank bool = false
|
||||
return &boundBool{val: &blank}
|
||||
}
|
||||
|
||||
// BindBool returns a new bindable value that controls the contents of the provided bool variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindBool(v *bool) ExternalBool {
|
||||
if v == nil {
|
||||
var blank bool = false
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalBool{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundBool struct {
|
||||
base
|
||||
|
||||
val *bool
|
||||
}
|
||||
|
||||
func (b *boundBool) Get() (bool, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return false, nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundBool) Set(val bool) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if *b.val == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalBool struct {
|
||||
boundBool
|
||||
|
||||
old bool
|
||||
}
|
||||
|
||||
func (b *boundExternalBool) Set(val bool) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.old == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalBool) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
||||
|
||||
// Bytes supports binding a []byte value.
|
||||
//
|
||||
// Since: 2.2
|
||||
type Bytes interface {
|
||||
DataItem
|
||||
Get() ([]byte, error)
|
||||
Set([]byte) error
|
||||
}
|
||||
|
||||
// ExternalBytes supports binding a []byte value to an external value.
|
||||
//
|
||||
// Since: 2.2
|
||||
type ExternalBytes interface {
|
||||
Bytes
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewBytes returns a bindable []byte value that is managed internally.
|
||||
//
|
||||
// Since: 2.2
|
||||
func NewBytes() Bytes {
|
||||
var blank []byte = nil
|
||||
return &boundBytes{val: &blank}
|
||||
}
|
||||
|
||||
// BindBytes returns a new bindable value that controls the contents of the provided []byte variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.2
|
||||
func BindBytes(v *[]byte) ExternalBytes {
|
||||
if v == nil {
|
||||
var blank []byte = nil
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalBytes{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundBytes struct {
|
||||
base
|
||||
|
||||
val *[]byte
|
||||
}
|
||||
|
||||
func (b *boundBytes) Get() ([]byte, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundBytes) Set(val []byte) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if bytes.Equal(*b.val, val) {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalBytes struct {
|
||||
boundBytes
|
||||
|
||||
old []byte
|
||||
}
|
||||
|
||||
func (b *boundExternalBytes) Set(val []byte) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if bytes.Equal(b.old, val) {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalBytes) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
||||
|
||||
// Float supports binding a float64 value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Float interface {
|
||||
DataItem
|
||||
Get() (float64, error)
|
||||
Set(float64) error
|
||||
}
|
||||
|
||||
// ExternalFloat supports binding a float64 value to an external value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type ExternalFloat interface {
|
||||
Float
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewFloat returns a bindable float64 value that is managed internally.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewFloat() Float {
|
||||
var blank float64 = 0.0
|
||||
return &boundFloat{val: &blank}
|
||||
}
|
||||
|
||||
// BindFloat returns a new bindable value that controls the contents of the provided float64 variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindFloat(v *float64) ExternalFloat {
|
||||
if v == nil {
|
||||
var blank float64 = 0.0
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalFloat{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundFloat struct {
|
||||
base
|
||||
|
||||
val *float64
|
||||
}
|
||||
|
||||
func (b *boundFloat) Get() (float64, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return 0.0, nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundFloat) Set(val float64) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if *b.val == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalFloat struct {
|
||||
boundFloat
|
||||
|
||||
old float64
|
||||
}
|
||||
|
||||
func (b *boundExternalFloat) Set(val float64) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.old == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalFloat) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
||||
|
||||
// Int supports binding a int value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Int interface {
|
||||
DataItem
|
||||
Get() (int, error)
|
||||
Set(int) error
|
||||
}
|
||||
|
||||
// ExternalInt supports binding a int value to an external value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type ExternalInt interface {
|
||||
Int
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewInt returns a bindable int value that is managed internally.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewInt() Int {
|
||||
var blank int = 0
|
||||
return &boundInt{val: &blank}
|
||||
}
|
||||
|
||||
// BindInt returns a new bindable value that controls the contents of the provided int variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindInt(v *int) ExternalInt {
|
||||
if v == nil {
|
||||
var blank int = 0
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalInt{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundInt struct {
|
||||
base
|
||||
|
||||
val *int
|
||||
}
|
||||
|
||||
func (b *boundInt) Get() (int, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundInt) Set(val int) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if *b.val == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalInt struct {
|
||||
boundInt
|
||||
|
||||
old int
|
||||
}
|
||||
|
||||
func (b *boundExternalInt) Set(val int) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.old == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalInt) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
||||
|
||||
// Rune supports binding a rune value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Rune interface {
|
||||
DataItem
|
||||
Get() (rune, error)
|
||||
Set(rune) error
|
||||
}
|
||||
|
||||
// ExternalRune supports binding a rune value to an external value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type ExternalRune interface {
|
||||
Rune
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewRune returns a bindable rune value that is managed internally.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewRune() Rune {
|
||||
var blank rune = rune(0)
|
||||
return &boundRune{val: &blank}
|
||||
}
|
||||
|
||||
// BindRune returns a new bindable value that controls the contents of the provided rune variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindRune(v *rune) ExternalRune {
|
||||
if v == nil {
|
||||
var blank rune = rune(0)
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalRune{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundRune struct {
|
||||
base
|
||||
|
||||
val *rune
|
||||
}
|
||||
|
||||
func (b *boundRune) Get() (rune, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return rune(0), nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundRune) Set(val rune) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if *b.val == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalRune struct {
|
||||
boundRune
|
||||
|
||||
old rune
|
||||
}
|
||||
|
||||
func (b *boundExternalRune) Set(val rune) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.old == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalRune) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
||||
|
||||
// String supports binding a string value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type String interface {
|
||||
DataItem
|
||||
Get() (string, error)
|
||||
Set(string) error
|
||||
}
|
||||
|
||||
// ExternalString supports binding a string value to an external value.
|
||||
//
|
||||
// Since: 2.0
|
||||
type ExternalString interface {
|
||||
String
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewString returns a bindable string value that is managed internally.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewString() String {
|
||||
var blank string = ""
|
||||
return &boundString{val: &blank}
|
||||
}
|
||||
|
||||
// BindString returns a new bindable value that controls the contents of the provided string variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindString(v *string) ExternalString {
|
||||
if v == nil {
|
||||
var blank string = ""
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalString{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundString struct {
|
||||
base
|
||||
|
||||
val *string
|
||||
}
|
||||
|
||||
func (b *boundString) Get() (string, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return "", nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundString) Set(val string) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if *b.val == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalString struct {
|
||||
boundString
|
||||
|
||||
old string
|
||||
}
|
||||
|
||||
func (b *boundExternalString) Set(val string) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if b.old == val {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalString) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
||||
|
||||
// URI supports binding a fyne.URI value.
|
||||
//
|
||||
// Since: 2.1
|
||||
type URI interface {
|
||||
DataItem
|
||||
Get() (fyne.URI, error)
|
||||
Set(fyne.URI) error
|
||||
}
|
||||
|
||||
// ExternalURI supports binding a fyne.URI value to an external value.
|
||||
//
|
||||
// Since: 2.1
|
||||
type ExternalURI interface {
|
||||
URI
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// NewURI returns a bindable fyne.URI value that is managed internally.
|
||||
//
|
||||
// Since: 2.1
|
||||
func NewURI() URI {
|
||||
var blank fyne.URI = fyne.URI(nil)
|
||||
return &boundURI{val: &blank}
|
||||
}
|
||||
|
||||
// BindURI returns a new bindable value that controls the contents of the provided fyne.URI variable.
|
||||
// If your code changes the content of the variable this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.1
|
||||
func BindURI(v *fyne.URI) ExternalURI {
|
||||
if v == nil {
|
||||
var blank fyne.URI = fyne.URI(nil)
|
||||
v = &blank // never allow a nil value pointer
|
||||
}
|
||||
b := &boundExternalURI{}
|
||||
b.val = v
|
||||
b.old = *v
|
||||
return b
|
||||
}
|
||||
|
||||
type boundURI struct {
|
||||
base
|
||||
|
||||
val *fyne.URI
|
||||
}
|
||||
|
||||
func (b *boundURI) Get() (fyne.URI, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return fyne.URI(nil), nil
|
||||
}
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *boundURI) Set(val fyne.URI) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if compareURI(*b.val, val) {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalURI struct {
|
||||
boundURI
|
||||
|
||||
old fyne.URI
|
||||
}
|
||||
|
||||
func (b *boundExternalURI) Set(val fyne.URI) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
if compareURI(b.old, val) {
|
||||
return nil
|
||||
}
|
||||
*b.val = val
|
||||
b.old = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *boundExternalURI) Reload() error {
|
||||
return b.Set(*b.val)
|
||||
}
|
1786
vendor/fyne.io/fyne/v2/data/binding/bindlists.go
generated
vendored
Normal file
1786
vendor/fyne.io/fyne/v2/data/binding/bindlists.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1816
vendor/fyne.io/fyne/v2/data/binding/bindtrees.go
generated
vendored
Normal file
1816
vendor/fyne.io/fyne/v2/data/binding/bindtrees.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
118
vendor/fyne.io/fyne/v2/data/binding/bool.go
generated
vendored
Normal file
118
vendor/fyne.io/fyne/v2/data/binding/bool.go
generated
vendored
Normal file
@ -0,0 +1,118 @@
|
||||
package binding
|
||||
|
||||
type not struct {
|
||||
Bool
|
||||
}
|
||||
|
||||
var _ Bool = (*not)(nil)
|
||||
|
||||
// Not returns a Bool binding that invert the value of the given data binding.
|
||||
// This is providing the logical Not boolean operation as a data binding.
|
||||
//
|
||||
// Since 2.4
|
||||
func Not(data Bool) Bool {
|
||||
return ¬{Bool: data}
|
||||
}
|
||||
|
||||
func (n *not) Get() (bool, error) {
|
||||
v, err := n.Bool.Get()
|
||||
return !v, err
|
||||
}
|
||||
|
||||
func (n *not) Set(value bool) error {
|
||||
return n.Bool.Set(!value)
|
||||
}
|
||||
|
||||
type and struct {
|
||||
booleans
|
||||
}
|
||||
|
||||
var _ Bool = (*and)(nil)
|
||||
|
||||
// And returns a Bool binding that return true when all the passed Bool binding are
|
||||
// true and false otherwise. It does apply a logical and boolean operation on all passed
|
||||
// Bool bindings. This binding is two way. In case of a Set, it will propagate the value
|
||||
// identically to all the Bool bindings used for its construction.
|
||||
//
|
||||
// Since 2.4
|
||||
func And(data ...Bool) Bool {
|
||||
return &and{booleans: booleans{data: data}}
|
||||
}
|
||||
|
||||
func (a *and) Get() (bool, error) {
|
||||
for _, d := range a.data {
|
||||
v, err := d.Get()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !v {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (a *and) Set(value bool) error {
|
||||
for _, d := range a.data {
|
||||
err := d.Set(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type or struct {
|
||||
booleans
|
||||
}
|
||||
|
||||
var _ Bool = (*or)(nil)
|
||||
|
||||
// Or returns a Bool binding that return true when at least one of the passed Bool binding
|
||||
// is true and false otherwise. It does apply a logical or boolean operation on all passed
|
||||
// Bool bindings. This binding is two way. In case of a Set, it will propagate the value
|
||||
// identically to all the Bool bindings used for its construction.
|
||||
//
|
||||
// Since 2.4
|
||||
func Or(data ...Bool) Bool {
|
||||
return &or{booleans: booleans{data: data}}
|
||||
}
|
||||
|
||||
func (o *or) Get() (bool, error) {
|
||||
for _, d := range o.data {
|
||||
v, err := d.Get()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if v {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (o *or) Set(value bool) error {
|
||||
for _, d := range o.data {
|
||||
err := d.Set(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type booleans struct {
|
||||
data []Bool
|
||||
}
|
||||
|
||||
func (g *booleans) AddListener(listener DataListener) {
|
||||
for _, d := range g.data {
|
||||
d.AddListener(listener)
|
||||
}
|
||||
}
|
||||
|
||||
func (g *booleans) RemoveListener(listener DataListener) {
|
||||
for _, d := range g.data {
|
||||
d.RemoveListener(listener)
|
||||
}
|
||||
}
|
13
vendor/fyne.io/fyne/v2/data/binding/comparator_helper.go
generated
vendored
Normal file
13
vendor/fyne.io/fyne/v2/data/binding/comparator_helper.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package binding
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
func compareURI(v1, v2 fyne.URI) bool {
|
||||
if v1 == nil && v1 == v2 {
|
||||
return true
|
||||
}
|
||||
if v1 == nil || v2 == nil {
|
||||
return false
|
||||
}
|
||||
return v1.String() == v2.String()
|
||||
}
|
638
vendor/fyne.io/fyne/v2/data/binding/convert.go
generated
vendored
Normal file
638
vendor/fyne.io/fyne/v2/data/binding/convert.go
generated
vendored
Normal file
@ -0,0 +1,638 @@
|
||||
// auto-generated
|
||||
// **** THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT IT **** //
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
type stringFromBool struct {
|
||||
base
|
||||
|
||||
format string
|
||||
|
||||
from Bool
|
||||
}
|
||||
|
||||
// BoolToString creates a binding that connects a Bool data item to a String.
|
||||
// Changes to the Bool will be pushed to the String and setting the string will parse and set the
|
||||
// Bool if the parse was successful.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BoolToString(v Bool) String {
|
||||
str := &stringFromBool{from: v}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
// BoolToStringWithFormat creates a binding that connects a Bool data item to a String and is
|
||||
// presented using the specified format. Changes to the Bool will be pushed to the String and setting
|
||||
// the string will parse and set the Bool if the string matches the format and its parse was successful.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BoolToStringWithFormat(v Bool, format string) String {
|
||||
if format == "%t" { // Same as not using custom formatting.
|
||||
return BoolToString(v)
|
||||
}
|
||||
|
||||
str := &stringFromBool{from: v, format: format}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s *stringFromBool) Get() (string, error) {
|
||||
val, err := s.from.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if s.format != "" {
|
||||
return fmt.Sprintf(s.format, val), nil
|
||||
}
|
||||
|
||||
return formatBool(val), nil
|
||||
}
|
||||
|
||||
func (s *stringFromBool) Set(str string) error {
|
||||
var val bool
|
||||
if s.format != "" {
|
||||
safe := stripFormatPrecision(s.format)
|
||||
n, err := fmt.Sscanf(str, safe+" ", &val) // " " denotes match to end of string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return errParseFailed
|
||||
}
|
||||
} else {
|
||||
new, err := parseBool(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val = new
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val == old {
|
||||
return nil
|
||||
}
|
||||
if err = s.from.Set(val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringFromBool) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringFromFloat struct {
|
||||
base
|
||||
|
||||
format string
|
||||
|
||||
from Float
|
||||
}
|
||||
|
||||
// FloatToString creates a binding that connects a Float data item to a String.
|
||||
// Changes to the Float will be pushed to the String and setting the string will parse and set the
|
||||
// Float if the parse was successful.
|
||||
//
|
||||
// Since: 2.0
|
||||
func FloatToString(v Float) String {
|
||||
str := &stringFromFloat{from: v}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
// FloatToStringWithFormat creates a binding that connects a Float data item to a String and is
|
||||
// presented using the specified format. Changes to the Float will be pushed to the String and setting
|
||||
// the string will parse and set the Float if the string matches the format and its parse was successful.
|
||||
//
|
||||
// Since: 2.0
|
||||
func FloatToStringWithFormat(v Float, format string) String {
|
||||
if format == "%f" { // Same as not using custom formatting.
|
||||
return FloatToString(v)
|
||||
}
|
||||
|
||||
str := &stringFromFloat{from: v, format: format}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s *stringFromFloat) Get() (string, error) {
|
||||
val, err := s.from.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if s.format != "" {
|
||||
return fmt.Sprintf(s.format, val), nil
|
||||
}
|
||||
|
||||
return formatFloat(val), nil
|
||||
}
|
||||
|
||||
func (s *stringFromFloat) Set(str string) error {
|
||||
var val float64
|
||||
if s.format != "" {
|
||||
safe := stripFormatPrecision(s.format)
|
||||
n, err := fmt.Sscanf(str, safe+" ", &val) // " " denotes match to end of string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return errParseFailed
|
||||
}
|
||||
} else {
|
||||
new, err := parseFloat(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val = new
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val == old {
|
||||
return nil
|
||||
}
|
||||
if err = s.from.Set(val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringFromFloat) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringFromInt struct {
|
||||
base
|
||||
|
||||
format string
|
||||
|
||||
from Int
|
||||
}
|
||||
|
||||
// IntToString creates a binding that connects a Int data item to a String.
|
||||
// Changes to the Int will be pushed to the String and setting the string will parse and set the
|
||||
// Int if the parse was successful.
|
||||
//
|
||||
// Since: 2.0
|
||||
func IntToString(v Int) String {
|
||||
str := &stringFromInt{from: v}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
// IntToStringWithFormat creates a binding that connects a Int data item to a String and is
|
||||
// presented using the specified format. Changes to the Int will be pushed to the String and setting
|
||||
// the string will parse and set the Int if the string matches the format and its parse was successful.
|
||||
//
|
||||
// Since: 2.0
|
||||
func IntToStringWithFormat(v Int, format string) String {
|
||||
if format == "%d" { // Same as not using custom formatting.
|
||||
return IntToString(v)
|
||||
}
|
||||
|
||||
str := &stringFromInt{from: v, format: format}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s *stringFromInt) Get() (string, error) {
|
||||
val, err := s.from.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if s.format != "" {
|
||||
return fmt.Sprintf(s.format, val), nil
|
||||
}
|
||||
|
||||
return formatInt(val), nil
|
||||
}
|
||||
|
||||
func (s *stringFromInt) Set(str string) error {
|
||||
var val int
|
||||
if s.format != "" {
|
||||
safe := stripFormatPrecision(s.format)
|
||||
n, err := fmt.Sscanf(str, safe+" ", &val) // " " denotes match to end of string
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != 1 {
|
||||
return errParseFailed
|
||||
}
|
||||
} else {
|
||||
new, err := parseInt(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
val = new
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val == old {
|
||||
return nil
|
||||
}
|
||||
if err = s.from.Set(val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringFromInt) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringFromURI struct {
|
||||
base
|
||||
|
||||
from URI
|
||||
}
|
||||
|
||||
// URIToString creates a binding that connects a URI data item to a String.
|
||||
// Changes to the URI will be pushed to the String and setting the string will parse and set the
|
||||
// URI if the parse was successful.
|
||||
//
|
||||
// Since: 2.1
|
||||
func URIToString(v URI) String {
|
||||
str := &stringFromURI{from: v}
|
||||
v.AddListener(str)
|
||||
return str
|
||||
}
|
||||
|
||||
func (s *stringFromURI) Get() (string, error) {
|
||||
val, err := s.from.Get()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return uriToString(val)
|
||||
}
|
||||
|
||||
func (s *stringFromURI) Set(str string) error {
|
||||
val, err := uriFromString(str)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if val == old {
|
||||
return nil
|
||||
}
|
||||
if err = s.from.Set(val); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringFromURI) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringToBool struct {
|
||||
base
|
||||
|
||||
format string
|
||||
|
||||
from String
|
||||
}
|
||||
|
||||
// StringToBool creates a binding that connects a String data item to a Bool.
|
||||
// Changes to the String will be parsed and pushed to the Bool if the parse was successful, and setting
|
||||
// the Bool update the String binding.
|
||||
//
|
||||
// Since: 2.0
|
||||
func StringToBool(str String) Bool {
|
||||
v := &stringToBool{from: str}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// StringToBoolWithFormat creates a binding that connects a String data item to a Bool and is
|
||||
// presented using the specified format. Changes to the Bool will be parsed and if the format matches and
|
||||
// the parse is successful it will be pushed to the String. Setting the Bool will push a formatted value
|
||||
// into the String.
|
||||
//
|
||||
// Since: 2.0
|
||||
func StringToBoolWithFormat(str String, format string) Bool {
|
||||
if format == "%t" { // Same as not using custom format.
|
||||
return StringToBool(str)
|
||||
}
|
||||
|
||||
v := &stringToBool{from: str, format: format}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *stringToBool) Get() (bool, error) {
|
||||
str, err := s.from.Get()
|
||||
if str == "" || err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var val bool
|
||||
if s.format != "" {
|
||||
n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if n != 1 {
|
||||
return false, errParseFailed
|
||||
}
|
||||
} else {
|
||||
new, err := parseBool(str)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
val = new
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (s *stringToBool) Set(val bool) error {
|
||||
var str string
|
||||
if s.format != "" {
|
||||
str = fmt.Sprintf(s.format, val)
|
||||
} else {
|
||||
str = formatBool(val)
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if str == old {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.from.Set(str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringToBool) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringToFloat struct {
|
||||
base
|
||||
|
||||
format string
|
||||
|
||||
from String
|
||||
}
|
||||
|
||||
// StringToFloat creates a binding that connects a String data item to a Float.
|
||||
// Changes to the String will be parsed and pushed to the Float if the parse was successful, and setting
|
||||
// the Float update the String binding.
|
||||
//
|
||||
// Since: 2.0
|
||||
func StringToFloat(str String) Float {
|
||||
v := &stringToFloat{from: str}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// StringToFloatWithFormat creates a binding that connects a String data item to a Float and is
|
||||
// presented using the specified format. Changes to the Float will be parsed and if the format matches and
|
||||
// the parse is successful it will be pushed to the String. Setting the Float will push a formatted value
|
||||
// into the String.
|
||||
//
|
||||
// Since: 2.0
|
||||
func StringToFloatWithFormat(str String, format string) Float {
|
||||
if format == "%f" { // Same as not using custom format.
|
||||
return StringToFloat(str)
|
||||
}
|
||||
|
||||
v := &stringToFloat{from: str, format: format}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *stringToFloat) Get() (float64, error) {
|
||||
str, err := s.from.Get()
|
||||
if str == "" || err != nil {
|
||||
return 0.0, err
|
||||
}
|
||||
|
||||
var val float64
|
||||
if s.format != "" {
|
||||
n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string
|
||||
if err != nil {
|
||||
return 0.0, err
|
||||
}
|
||||
if n != 1 {
|
||||
return 0.0, errParseFailed
|
||||
}
|
||||
} else {
|
||||
new, err := parseFloat(str)
|
||||
if err != nil {
|
||||
return 0.0, err
|
||||
}
|
||||
val = new
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (s *stringToFloat) Set(val float64) error {
|
||||
var str string
|
||||
if s.format != "" {
|
||||
str = fmt.Sprintf(s.format, val)
|
||||
} else {
|
||||
str = formatFloat(val)
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if str == old {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.from.Set(str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringToFloat) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringToInt struct {
|
||||
base
|
||||
|
||||
format string
|
||||
|
||||
from String
|
||||
}
|
||||
|
||||
// StringToInt creates a binding that connects a String data item to a Int.
|
||||
// Changes to the String will be parsed and pushed to the Int if the parse was successful, and setting
|
||||
// the Int update the String binding.
|
||||
//
|
||||
// Since: 2.0
|
||||
func StringToInt(str String) Int {
|
||||
v := &stringToInt{from: str}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
// StringToIntWithFormat creates a binding that connects a String data item to a Int and is
|
||||
// presented using the specified format. Changes to the Int will be parsed and if the format matches and
|
||||
// the parse is successful it will be pushed to the String. Setting the Int will push a formatted value
|
||||
// into the String.
|
||||
//
|
||||
// Since: 2.0
|
||||
func StringToIntWithFormat(str String, format string) Int {
|
||||
if format == "%d" { // Same as not using custom format.
|
||||
return StringToInt(str)
|
||||
}
|
||||
|
||||
v := &stringToInt{from: str, format: format}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *stringToInt) Get() (int, error) {
|
||||
str, err := s.from.Get()
|
||||
if str == "" || err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var val int
|
||||
if s.format != "" {
|
||||
n, err := fmt.Sscanf(str, s.format+" ", &val) // " " denotes match to end of string
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if n != 1 {
|
||||
return 0, errParseFailed
|
||||
}
|
||||
} else {
|
||||
new, err := parseInt(str)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
val = new
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
||||
|
||||
func (s *stringToInt) Set(val int) error {
|
||||
var str string
|
||||
if s.format != "" {
|
||||
str = fmt.Sprintf(s.format, val)
|
||||
} else {
|
||||
str = formatInt(val)
|
||||
}
|
||||
|
||||
old, err := s.from.Get()
|
||||
if str == old {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.from.Set(str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringToInt) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
||||
|
||||
type stringToURI struct {
|
||||
base
|
||||
|
||||
from String
|
||||
}
|
||||
|
||||
// StringToURI creates a binding that connects a String data item to a URI.
|
||||
// Changes to the String will be parsed and pushed to the URI if the parse was successful, and setting
|
||||
// the URI update the String binding.
|
||||
//
|
||||
// Since: 2.1
|
||||
func StringToURI(str String) URI {
|
||||
v := &stringToURI{from: str}
|
||||
str.AddListener(v)
|
||||
return v
|
||||
}
|
||||
|
||||
func (s *stringToURI) Get() (fyne.URI, error) {
|
||||
str, err := s.from.Get()
|
||||
if str == "" || err != nil {
|
||||
return fyne.URI(nil), err
|
||||
}
|
||||
|
||||
return uriFromString(str)
|
||||
}
|
||||
|
||||
func (s *stringToURI) Set(val fyne.URI) error {
|
||||
str, err := uriToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
old, err := s.from.Get()
|
||||
if str == old {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.from.Set(str); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.DataChanged()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stringToURI) DataChanged() {
|
||||
s.lock.RLock()
|
||||
defer s.lock.RUnlock()
|
||||
s.trigger()
|
||||
}
|
103
vendor/fyne.io/fyne/v2/data/binding/convert_helper.go
generated
vendored
Normal file
103
vendor/fyne.io/fyne/v2/data/binding/convert_helper.go
generated
vendored
Normal file
@ -0,0 +1,103 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/storage"
|
||||
)
|
||||
|
||||
func stripFormatPrecision(in string) string {
|
||||
// quick exit if certainly not float
|
||||
if !strings.ContainsAny(in, "f") {
|
||||
return in
|
||||
}
|
||||
|
||||
start := -1
|
||||
end := -1
|
||||
runes := []rune(in)
|
||||
for i, r := range runes {
|
||||
switch r {
|
||||
case '%':
|
||||
if i > 0 && start == i-1 { // ignore %%
|
||||
start = -1
|
||||
} else {
|
||||
start = i
|
||||
}
|
||||
case 'f':
|
||||
if start == -1 { // not part of format
|
||||
continue
|
||||
}
|
||||
end = i
|
||||
}
|
||||
|
||||
if end > -1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if end == start+1 { // no width/precision
|
||||
return in
|
||||
}
|
||||
|
||||
sizeRunes := runes[start+1 : end]
|
||||
width, err := parseFloat(string(sizeRunes))
|
||||
if err != nil {
|
||||
return string(runes[:start+1]) + string(runes[:end])
|
||||
}
|
||||
|
||||
if sizeRunes[0] == '.' { // formats like %.2f
|
||||
return string(runes[:start+1]) + string(runes[end:])
|
||||
}
|
||||
return string(runes[:start+1]) + strconv.Itoa(int(width)) + string(runes[end:])
|
||||
}
|
||||
|
||||
func uriFromString(in string) (fyne.URI, error) {
|
||||
return storage.ParseURI(in)
|
||||
}
|
||||
|
||||
func uriToString(in fyne.URI) (string, error) {
|
||||
if in == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return in.String(), nil
|
||||
}
|
||||
|
||||
func parseBool(in string) (bool, error) {
|
||||
out, err := strconv.ParseBool(in)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseFloat(in string) (float64, error) {
|
||||
out, err := strconv.ParseFloat(in, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func parseInt(in string) (int, error) {
|
||||
out, err := strconv.ParseInt(in, 0, 64)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(out), nil
|
||||
}
|
||||
|
||||
func formatBool(in bool) string {
|
||||
return strconv.FormatBool(in)
|
||||
}
|
||||
|
||||
func formatFloat(in float64) string {
|
||||
return strconv.FormatFloat(in, 'f', 6, 64)
|
||||
}
|
||||
|
||||
func formatInt(in int) string {
|
||||
return strconv.FormatInt(int64(in), 10)
|
||||
}
|
43
vendor/fyne.io/fyne/v2/data/binding/listbinding.go
generated
vendored
Normal file
43
vendor/fyne.io/fyne/v2/data/binding/listbinding.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package binding
|
||||
|
||||
// DataList is the base interface for all bindable data lists.
|
||||
//
|
||||
// Since: 2.0
|
||||
type DataList interface {
|
||||
DataItem
|
||||
GetItem(index int) (DataItem, error)
|
||||
Length() int
|
||||
}
|
||||
|
||||
type listBase struct {
|
||||
base
|
||||
items []DataItem
|
||||
}
|
||||
|
||||
// GetItem returns the DataItem at the specified index.
|
||||
func (b *listBase) GetItem(i int) (DataItem, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if i < 0 || i >= len(b.items) {
|
||||
return nil, errOutOfBounds
|
||||
}
|
||||
|
||||
return b.items[i], nil
|
||||
}
|
||||
|
||||
// Length returns the number of items in this data list.
|
||||
func (b *listBase) Length() int {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
return len(b.items)
|
||||
}
|
||||
|
||||
func (b *listBase) appendItem(i DataItem) {
|
||||
b.items = append(b.items, i)
|
||||
}
|
||||
|
||||
func (b *listBase) deleteItem(i int) {
|
||||
b.items = append(b.items[:i], b.items[i+1:]...)
|
||||
}
|
522
vendor/fyne.io/fyne/v2/data/binding/mapbinding.go
generated
vendored
Normal file
522
vendor/fyne.io/fyne/v2/data/binding/mapbinding.go
generated
vendored
Normal file
@ -0,0 +1,522 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// DataMap is the base interface for all bindable data maps.
|
||||
//
|
||||
// Since: 2.0
|
||||
type DataMap interface {
|
||||
DataItem
|
||||
GetItem(string) (DataItem, error)
|
||||
Keys() []string
|
||||
}
|
||||
|
||||
// ExternalUntypedMap is a map data binding with all values untyped (interface{}), connected to an external data source.
|
||||
//
|
||||
// Since: 2.0
|
||||
type ExternalUntypedMap interface {
|
||||
UntypedMap
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// UntypedMap is a map data binding with all values Untyped (interface{}).
|
||||
//
|
||||
// Since: 2.0
|
||||
type UntypedMap interface {
|
||||
DataMap
|
||||
Delete(string)
|
||||
Get() (map[string]interface{}, error)
|
||||
GetValue(string) (interface{}, error)
|
||||
Set(map[string]interface{}) error
|
||||
SetValue(string, interface{}) error
|
||||
}
|
||||
|
||||
// NewUntypedMap creates a new, empty map binding of string to interface{}.
|
||||
//
|
||||
// Since: 2.0
|
||||
func NewUntypedMap() UntypedMap {
|
||||
return &mapBase{items: make(map[string]reflectUntyped), val: &map[string]interface{}{}}
|
||||
}
|
||||
|
||||
// BindUntypedMap creates a new map binding of string to interface{} based on the data passed.
|
||||
// If your code changes the content of the map this refers to you should call Reload() to inform the bindings.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindUntypedMap(d *map[string]interface{}) ExternalUntypedMap {
|
||||
if d == nil {
|
||||
return NewUntypedMap().(ExternalUntypedMap)
|
||||
}
|
||||
m := &mapBase{items: make(map[string]reflectUntyped), val: d, updateExternal: true}
|
||||
|
||||
for k := range *d {
|
||||
m.setItem(k, bindUntypedMapValue(d, k, m.updateExternal))
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// Struct is the base interface for a bound struct type.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Struct interface {
|
||||
DataMap
|
||||
GetValue(string) (interface{}, error)
|
||||
SetValue(string, interface{}) error
|
||||
Reload() error
|
||||
}
|
||||
|
||||
// BindStruct creates a new map binding of string to interface{} using the struct passed as data.
|
||||
// The key for each item is a string representation of each exported field with the value set as an interface{}.
|
||||
// Only exported fields are included.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindStruct(i interface{}) Struct {
|
||||
if i == nil {
|
||||
return NewUntypedMap().(Struct)
|
||||
}
|
||||
t := reflect.TypeOf(i)
|
||||
if t.Kind() != reflect.Ptr ||
|
||||
(reflect.TypeOf(reflect.ValueOf(i).Elem()).Kind() != reflect.Struct) {
|
||||
fyne.LogError("Invalid type passed to BindStruct, must be pointer to struct", nil)
|
||||
return NewUntypedMap().(Struct)
|
||||
}
|
||||
|
||||
s := &boundStruct{orig: i}
|
||||
s.items = make(map[string]reflectUntyped)
|
||||
s.val = &map[string]interface{}{}
|
||||
s.updateExternal = true
|
||||
|
||||
v := reflect.ValueOf(i).Elem()
|
||||
t = v.Type()
|
||||
for j := 0; j < v.NumField(); j++ {
|
||||
f := v.Field(j)
|
||||
if !f.CanSet() {
|
||||
continue
|
||||
}
|
||||
|
||||
key := t.Field(j).Name
|
||||
s.items[key] = bindReflect(f)
|
||||
(*s.val)[key] = f.Interface()
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
type reflectUntyped interface {
|
||||
DataItem
|
||||
get() (interface{}, error)
|
||||
set(interface{}) error
|
||||
}
|
||||
|
||||
type mapBase struct {
|
||||
base
|
||||
|
||||
updateExternal bool
|
||||
items map[string]reflectUntyped
|
||||
val *map[string]interface{}
|
||||
}
|
||||
|
||||
func (b *mapBase) GetItem(key string) (DataItem, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if v, ok := b.items[key]; ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return nil, errKeyNotFound
|
||||
}
|
||||
|
||||
func (b *mapBase) Keys() []string {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
ret := make([]string, len(b.items))
|
||||
i := 0
|
||||
for k := range b.items {
|
||||
ret[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *mapBase) Delete(key string) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
delete(b.items, key)
|
||||
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
func (b *mapBase) Get() (map[string]interface{}, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if b.val == nil {
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
return *b.val, nil
|
||||
}
|
||||
|
||||
func (b *mapBase) GetValue(key string) (interface{}, error) {
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
|
||||
if i, ok := b.items[key]; ok {
|
||||
return i.get()
|
||||
}
|
||||
|
||||
return nil, errKeyNotFound
|
||||
}
|
||||
|
||||
func (b *mapBase) Reload() error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
return b.doReload()
|
||||
}
|
||||
|
||||
func (b *mapBase) Set(v map[string]interface{}) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
if b.val == nil { // was not initialized with a blank value, recover
|
||||
b.val = &v
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
*b.val = v
|
||||
return b.doReload()
|
||||
}
|
||||
|
||||
func (b *mapBase) SetValue(key string, d interface{}) error {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
if i, ok := b.items[key]; ok {
|
||||
return i.set(d)
|
||||
}
|
||||
|
||||
(*b.val)[key] = d
|
||||
item := bindUntypedMapValue(b.val, key, b.updateExternal)
|
||||
b.setItem(key, item)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *mapBase) doReload() (retErr error) {
|
||||
changed := false
|
||||
// add new
|
||||
for key := range *b.val {
|
||||
_, found := b.items[key]
|
||||
if !found {
|
||||
b.setItem(key, bindUntypedMapValue(b.val, key, b.updateExternal))
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
|
||||
// remove old
|
||||
for key := range b.items {
|
||||
_, found := (*b.val)[key]
|
||||
if !found {
|
||||
delete(b.items, key)
|
||||
changed = true
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
for k, item := range b.items {
|
||||
var err error
|
||||
|
||||
if b.updateExternal {
|
||||
err = item.(*boundExternalMapValue).setIfChanged((*b.val)[k])
|
||||
} else {
|
||||
err = item.(*boundMapValue).set((*b.val)[k])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
retErr = err
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *mapBase) setItem(key string, d reflectUntyped) {
|
||||
b.items[key] = d
|
||||
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
type boundStruct struct {
|
||||
mapBase
|
||||
|
||||
orig interface{}
|
||||
}
|
||||
|
||||
func (b *boundStruct) Reload() (retErr error) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
v := reflect.ValueOf(b.orig).Elem()
|
||||
t := v.Type()
|
||||
for j := 0; j < v.NumField(); j++ {
|
||||
f := v.Field(j)
|
||||
if !f.CanSet() {
|
||||
continue
|
||||
}
|
||||
kind := f.Kind()
|
||||
if kind == reflect.Slice || kind == reflect.Struct {
|
||||
fyne.LogError("Data binding does not yet support slice or struct elements in a struct", nil)
|
||||
continue
|
||||
}
|
||||
|
||||
key := t.Field(j).Name
|
||||
old := (*b.val)[key]
|
||||
if f.Interface() == old {
|
||||
continue
|
||||
}
|
||||
|
||||
var err error
|
||||
switch kind {
|
||||
case reflect.Bool:
|
||||
err = b.items[key].(*reflectBool).Set(f.Bool())
|
||||
case reflect.Float32, reflect.Float64:
|
||||
err = b.items[key].(*reflectFloat).Set(f.Float())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
err = b.items[key].(*reflectInt).Set(int(f.Int()))
|
||||
case reflect.String:
|
||||
err = b.items[key].(*reflectString).Set(f.String())
|
||||
}
|
||||
if err != nil {
|
||||
retErr = err
|
||||
}
|
||||
(*b.val)[key] = f.Interface()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bindUntypedMapValue(m *map[string]interface{}, k string, external bool) reflectUntyped {
|
||||
if external {
|
||||
ret := &boundExternalMapValue{old: (*m)[k]}
|
||||
ret.val = m
|
||||
ret.key = k
|
||||
return ret
|
||||
}
|
||||
|
||||
return &boundMapValue{val: m, key: k}
|
||||
}
|
||||
|
||||
type boundMapValue struct {
|
||||
base
|
||||
|
||||
val *map[string]interface{}
|
||||
key string
|
||||
}
|
||||
|
||||
func (b *boundMapValue) get() (interface{}, error) {
|
||||
if v, ok := (*b.val)[b.key]; ok {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
return nil, errKeyNotFound
|
||||
}
|
||||
|
||||
func (b *boundMapValue) set(val interface{}) error {
|
||||
(*b.val)[b.key] = val
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type boundExternalMapValue struct {
|
||||
boundMapValue
|
||||
|
||||
old interface{}
|
||||
}
|
||||
|
||||
func (b *boundExternalMapValue) setIfChanged(val interface{}) error {
|
||||
if val == b.old {
|
||||
return nil
|
||||
}
|
||||
b.old = val
|
||||
|
||||
return b.set(val)
|
||||
}
|
||||
|
||||
type boundReflect struct {
|
||||
base
|
||||
|
||||
val reflect.Value
|
||||
}
|
||||
|
||||
func (b *boundReflect) get() (interface{}, error) {
|
||||
return b.val.Interface(), nil
|
||||
}
|
||||
|
||||
func (b *boundReflect) set(val interface{}) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("unable to set bool in data binding")
|
||||
}
|
||||
}()
|
||||
b.val.Set(reflect.ValueOf(val))
|
||||
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
type reflectBool struct {
|
||||
boundReflect
|
||||
}
|
||||
|
||||
func (r *reflectBool) Get() (val bool, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("invalid bool value in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
val = r.val.Bool()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *reflectBool) Set(b bool) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("unable to set bool in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
r.val.SetBool(b)
|
||||
r.trigger()
|
||||
return
|
||||
}
|
||||
|
||||
func bindReflectBool(f reflect.Value) reflectUntyped {
|
||||
r := &reflectBool{}
|
||||
r.val = f
|
||||
return r
|
||||
}
|
||||
|
||||
type reflectFloat struct {
|
||||
boundReflect
|
||||
}
|
||||
|
||||
func (r *reflectFloat) Get() (val float64, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("invalid float64 value in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
val = r.val.Float()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *reflectFloat) Set(f float64) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("unable to set float64 in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
r.val.SetFloat(f)
|
||||
r.trigger()
|
||||
return
|
||||
}
|
||||
|
||||
func bindReflectFloat(f reflect.Value) reflectUntyped {
|
||||
r := &reflectFloat{}
|
||||
r.val = f
|
||||
return r
|
||||
}
|
||||
|
||||
type reflectInt struct {
|
||||
boundReflect
|
||||
}
|
||||
|
||||
func (r *reflectInt) Get() (val int, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("invalid int value in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
val = int(r.val.Int())
|
||||
return
|
||||
}
|
||||
|
||||
func (r *reflectInt) Set(i int) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("unable to set int in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
r.val.SetInt(int64(i))
|
||||
r.trigger()
|
||||
return
|
||||
}
|
||||
|
||||
func bindReflectInt(f reflect.Value) reflectUntyped {
|
||||
r := &reflectInt{}
|
||||
r.val = f
|
||||
return r
|
||||
}
|
||||
|
||||
type reflectString struct {
|
||||
boundReflect
|
||||
}
|
||||
|
||||
func (r *reflectString) Get() (val string, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("invalid string value in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
val = r.val.String()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *reflectString) Set(s string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.New("unable to set string in data binding")
|
||||
}
|
||||
}()
|
||||
|
||||
r.val.SetString(s)
|
||||
r.trigger()
|
||||
return
|
||||
}
|
||||
|
||||
func bindReflectString(f reflect.Value) reflectUntyped {
|
||||
r := &reflectString{}
|
||||
r.val = f
|
||||
return r
|
||||
}
|
||||
|
||||
func bindReflect(field reflect.Value) reflectUntyped {
|
||||
switch field.Kind() {
|
||||
case reflect.Bool:
|
||||
return bindReflectBool(field)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return bindReflectFloat(field)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return bindReflectInt(field)
|
||||
case reflect.String:
|
||||
return bindReflectString(field)
|
||||
}
|
||||
return &boundReflect{val: field}
|
||||
}
|
104
vendor/fyne.io/fyne/v2/data/binding/pref_helper.go
generated
vendored
Normal file
104
vendor/fyne.io/fyne/v2/data/binding/pref_helper.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
type preferenceItem interface {
|
||||
checkForChange()
|
||||
}
|
||||
|
||||
type preferenceBindings struct {
|
||||
items sync.Map // map[string]preferenceItem
|
||||
}
|
||||
|
||||
func (b *preferenceBindings) getItem(key string) preferenceItem {
|
||||
val, loaded := b.items.Load(key)
|
||||
if !loaded {
|
||||
return nil
|
||||
}
|
||||
return val.(preferenceItem)
|
||||
}
|
||||
|
||||
func (b *preferenceBindings) list() []preferenceItem {
|
||||
ret := []preferenceItem{}
|
||||
b.items.Range(func(_, val interface{}) bool {
|
||||
ret = append(ret, val.(preferenceItem))
|
||||
return true
|
||||
})
|
||||
return ret
|
||||
}
|
||||
|
||||
func (b *preferenceBindings) setItem(key string, item preferenceItem) {
|
||||
b.items.Store(key, item)
|
||||
}
|
||||
|
||||
type preferencesMap struct {
|
||||
prefs sync.Map // map[fyne.Preferences]*preferenceBindings
|
||||
|
||||
appPrefs fyne.Preferences // the main application prefs, to check if it changed...
|
||||
}
|
||||
|
||||
func newPreferencesMap() *preferencesMap {
|
||||
return &preferencesMap{}
|
||||
}
|
||||
|
||||
func (m *preferencesMap) ensurePreferencesAttached(p fyne.Preferences) *preferenceBindings {
|
||||
binds, loaded := m.prefs.LoadOrStore(p, &preferenceBindings{})
|
||||
if loaded {
|
||||
return binds.(*preferenceBindings)
|
||||
}
|
||||
|
||||
p.AddChangeListener(func() { m.preferencesChanged(fyne.CurrentApp().Preferences()) })
|
||||
return binds.(*preferenceBindings)
|
||||
}
|
||||
|
||||
func (m *preferencesMap) getBindings(p fyne.Preferences) *preferenceBindings {
|
||||
if p == fyne.CurrentApp().Preferences() {
|
||||
if m.appPrefs == nil {
|
||||
m.appPrefs = p
|
||||
} else if m.appPrefs != p {
|
||||
m.migratePreferences(m.appPrefs, p)
|
||||
}
|
||||
}
|
||||
binds, loaded := m.prefs.Load(p)
|
||||
if !loaded {
|
||||
return nil
|
||||
}
|
||||
return binds.(*preferenceBindings)
|
||||
}
|
||||
|
||||
func (m *preferencesMap) preferencesChanged(p fyne.Preferences) {
|
||||
binds := m.getBindings(p)
|
||||
if binds == nil {
|
||||
return
|
||||
}
|
||||
for _, item := range binds.list() {
|
||||
item.checkForChange()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *preferencesMap) migratePreferences(src, dst fyne.Preferences) {
|
||||
old, loaded := m.prefs.Load(src)
|
||||
if !loaded {
|
||||
return
|
||||
}
|
||||
|
||||
m.prefs.Store(dst, old)
|
||||
m.prefs.Delete(src)
|
||||
m.appPrefs = dst
|
||||
|
||||
binds := m.getBindings(dst)
|
||||
if binds == nil {
|
||||
return
|
||||
}
|
||||
for _, b := range binds.list() {
|
||||
if backed, ok := b.(interface{ replaceProvider(fyne.Preferences) }); ok {
|
||||
backed.replaceProvider(dst)
|
||||
}
|
||||
}
|
||||
|
||||
m.preferencesChanged(dst)
|
||||
}
|
244
vendor/fyne.io/fyne/v2/data/binding/preference.go
generated
vendored
Normal file
244
vendor/fyne.io/fyne/v2/data/binding/preference.go
generated
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
// auto-generated
|
||||
// **** THIS FILE IS AUTO-GENERATED, PLEASE DO NOT EDIT IT **** //
|
||||
|
||||
package binding
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
const keyTypeMismatchError = "A previous preference binding exists with different type for key: "
|
||||
|
||||
type prefBoundBool struct {
|
||||
base
|
||||
key string
|
||||
p fyne.Preferences
|
||||
cache atomic.Value // bool
|
||||
}
|
||||
|
||||
// BindPreferenceBool returns a bindable bool value that is managed by the application preferences.
|
||||
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindPreferenceBool(key string, p fyne.Preferences) Bool {
|
||||
binds := prefBinds.getBindings(p)
|
||||
if binds != nil {
|
||||
if listen := binds.getItem(key); listen != nil {
|
||||
if l, ok := listen.(Bool); ok {
|
||||
return l
|
||||
}
|
||||
fyne.LogError(keyTypeMismatchError+key, nil)
|
||||
}
|
||||
}
|
||||
|
||||
listen := &prefBoundBool{key: key, p: p}
|
||||
binds = prefBinds.ensurePreferencesAttached(p)
|
||||
binds.setItem(key, listen)
|
||||
return listen
|
||||
}
|
||||
|
||||
func (b *prefBoundBool) Get() (bool, error) {
|
||||
cache := b.p.Bool(b.key)
|
||||
b.cache.Store(cache)
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (b *prefBoundBool) Set(v bool) error {
|
||||
b.p.SetBool(b.key, v)
|
||||
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *prefBoundBool) checkForChange() {
|
||||
val := b.cache.Load()
|
||||
if val != nil {
|
||||
cache := val.(bool)
|
||||
if b.p.Bool(b.key) == cache {
|
||||
return
|
||||
}
|
||||
}
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
func (b *prefBoundBool) replaceProvider(p fyne.Preferences) {
|
||||
b.p = p
|
||||
}
|
||||
|
||||
type prefBoundFloat struct {
|
||||
base
|
||||
key string
|
||||
p fyne.Preferences
|
||||
cache atomic.Value // float64
|
||||
}
|
||||
|
||||
// BindPreferenceFloat returns a bindable float64 value that is managed by the application preferences.
|
||||
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindPreferenceFloat(key string, p fyne.Preferences) Float {
|
||||
binds := prefBinds.getBindings(p)
|
||||
if binds != nil {
|
||||
if listen := binds.getItem(key); listen != nil {
|
||||
if l, ok := listen.(Float); ok {
|
||||
return l
|
||||
}
|
||||
fyne.LogError(keyTypeMismatchError+key, nil)
|
||||
}
|
||||
}
|
||||
|
||||
listen := &prefBoundFloat{key: key, p: p}
|
||||
binds = prefBinds.ensurePreferencesAttached(p)
|
||||
binds.setItem(key, listen)
|
||||
return listen
|
||||
}
|
||||
|
||||
func (b *prefBoundFloat) Get() (float64, error) {
|
||||
cache := b.p.Float(b.key)
|
||||
b.cache.Store(cache)
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (b *prefBoundFloat) Set(v float64) error {
|
||||
b.p.SetFloat(b.key, v)
|
||||
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *prefBoundFloat) checkForChange() {
|
||||
val := b.cache.Load()
|
||||
if val != nil {
|
||||
cache := val.(float64)
|
||||
if b.p.Float(b.key) == cache {
|
||||
return
|
||||
}
|
||||
}
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
func (b *prefBoundFloat) replaceProvider(p fyne.Preferences) {
|
||||
b.p = p
|
||||
}
|
||||
|
||||
type prefBoundInt struct {
|
||||
base
|
||||
key string
|
||||
p fyne.Preferences
|
||||
cache atomic.Value // int
|
||||
}
|
||||
|
||||
// BindPreferenceInt returns a bindable int value that is managed by the application preferences.
|
||||
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindPreferenceInt(key string, p fyne.Preferences) Int {
|
||||
binds := prefBinds.getBindings(p)
|
||||
if binds != nil {
|
||||
if listen := binds.getItem(key); listen != nil {
|
||||
if l, ok := listen.(Int); ok {
|
||||
return l
|
||||
}
|
||||
fyne.LogError(keyTypeMismatchError+key, nil)
|
||||
}
|
||||
}
|
||||
|
||||
listen := &prefBoundInt{key: key, p: p}
|
||||
binds = prefBinds.ensurePreferencesAttached(p)
|
||||
binds.setItem(key, listen)
|
||||
return listen
|
||||
}
|
||||
|
||||
func (b *prefBoundInt) Get() (int, error) {
|
||||
cache := b.p.Int(b.key)
|
||||
b.cache.Store(cache)
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (b *prefBoundInt) Set(v int) error {
|
||||
b.p.SetInt(b.key, v)
|
||||
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *prefBoundInt) checkForChange() {
|
||||
val := b.cache.Load()
|
||||
if val != nil {
|
||||
cache := val.(int)
|
||||
if b.p.Int(b.key) == cache {
|
||||
return
|
||||
}
|
||||
}
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
func (b *prefBoundInt) replaceProvider(p fyne.Preferences) {
|
||||
b.p = p
|
||||
}
|
||||
|
||||
type prefBoundString struct {
|
||||
base
|
||||
key string
|
||||
p fyne.Preferences
|
||||
cache atomic.Value // string
|
||||
}
|
||||
|
||||
// BindPreferenceString returns a bindable string value that is managed by the application preferences.
|
||||
// Changes to this value will be saved to application storage and when the app starts the previous values will be read.
|
||||
//
|
||||
// Since: 2.0
|
||||
func BindPreferenceString(key string, p fyne.Preferences) String {
|
||||
binds := prefBinds.getBindings(p)
|
||||
if binds != nil {
|
||||
if listen := binds.getItem(key); listen != nil {
|
||||
if l, ok := listen.(String); ok {
|
||||
return l
|
||||
}
|
||||
fyne.LogError(keyTypeMismatchError+key, nil)
|
||||
}
|
||||
}
|
||||
|
||||
listen := &prefBoundString{key: key, p: p}
|
||||
binds = prefBinds.ensurePreferencesAttached(p)
|
||||
binds.setItem(key, listen)
|
||||
return listen
|
||||
}
|
||||
|
||||
func (b *prefBoundString) Get() (string, error) {
|
||||
cache := b.p.String(b.key)
|
||||
b.cache.Store(cache)
|
||||
return cache, nil
|
||||
}
|
||||
|
||||
func (b *prefBoundString) Set(v string) error {
|
||||
b.p.SetString(b.key, v)
|
||||
|
||||
b.lock.RLock()
|
||||
defer b.lock.RUnlock()
|
||||
b.trigger()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *prefBoundString) checkForChange() {
|
||||
val := b.cache.Load()
|
||||
if val != nil {
|
||||
cache := val.(string)
|
||||
if b.p.String(b.key) == cache {
|
||||
return
|
||||
}
|
||||
}
|
||||
b.trigger()
|
||||
}
|
||||
|
||||
func (b *prefBoundString) replaceProvider(p fyne.Preferences) {
|
||||
b.p = p
|
||||
}
|
30
vendor/fyne.io/fyne/v2/data/binding/queue.go
generated
vendored
Normal file
30
vendor/fyne.io/fyne/v2/data/binding/queue.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"fyne.io/fyne/v2/internal/async"
|
||||
)
|
||||
|
||||
var (
|
||||
once sync.Once
|
||||
queue *async.UnboundedFuncChan
|
||||
)
|
||||
|
||||
func queueItem(f func()) {
|
||||
once.Do(func() {
|
||||
queue = async.NewUnboundedFuncChan()
|
||||
go func() {
|
||||
for f := range queue.Out() {
|
||||
f()
|
||||
}
|
||||
}()
|
||||
})
|
||||
queue.In() <- f
|
||||
}
|
||||
|
||||
func waitForItems() {
|
||||
done := make(chan struct{})
|
||||
queue.In() <- func() { close(done) }
|
||||
<-done
|
||||
}
|
218
vendor/fyne.io/fyne/v2/data/binding/sprintf.go
generated
vendored
Normal file
218
vendor/fyne.io/fyne/v2/data/binding/sprintf.go
generated
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
package binding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"fyne.io/fyne/v2/storage"
|
||||
)
|
||||
|
||||
type sprintfString struct {
|
||||
String
|
||||
|
||||
format string
|
||||
source []DataItem
|
||||
err error
|
||||
}
|
||||
|
||||
// NewSprintf returns a String binding that format its content using the
|
||||
// format string and the provide additional parameter that must be other
|
||||
// data bindings. This data binding use fmt.Sprintf and fmt.Scanf internally
|
||||
// and will have all the same limitation as those function.
|
||||
//
|
||||
// Since: 2.2
|
||||
func NewSprintf(format string, b ...DataItem) String {
|
||||
ret := &sprintfString{
|
||||
String: NewString(),
|
||||
format: format,
|
||||
source: append(make([]DataItem, 0, len(b)), b...),
|
||||
}
|
||||
|
||||
for _, value := range b {
|
||||
value.AddListener(ret)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (s *sprintfString) DataChanged() {
|
||||
data := make([]interface{}, 0, len(s.source))
|
||||
|
||||
s.err = nil
|
||||
for _, value := range s.source {
|
||||
switch x := value.(type) {
|
||||
case Bool:
|
||||
b, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, b)
|
||||
case Bytes:
|
||||
b, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, b)
|
||||
case Float:
|
||||
f, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, f)
|
||||
case Int:
|
||||
i, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, i)
|
||||
case Rune:
|
||||
r, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, r)
|
||||
case String:
|
||||
str, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
// Set error?
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, str)
|
||||
case URI:
|
||||
u, err := x.Get()
|
||||
if err != nil {
|
||||
s.err = err
|
||||
return
|
||||
}
|
||||
|
||||
data = append(data, u)
|
||||
}
|
||||
}
|
||||
|
||||
r := fmt.Sprintf(s.format, data...)
|
||||
s.String.Set(r)
|
||||
}
|
||||
|
||||
func (s *sprintfString) Get() (string, error) {
|
||||
if s.err != nil {
|
||||
return "", s.err
|
||||
}
|
||||
return s.String.Get()
|
||||
}
|
||||
|
||||
func (s *sprintfString) Set(str string) error {
|
||||
data := make([]interface{}, 0, len(s.source))
|
||||
|
||||
s.err = nil
|
||||
for _, value := range s.source {
|
||||
switch value.(type) {
|
||||
case Bool:
|
||||
data = append(data, new(bool))
|
||||
case Bytes:
|
||||
return fmt.Errorf("impossible to convert '%s' to []bytes type", str)
|
||||
case Float:
|
||||
data = append(data, new(float64))
|
||||
case Int:
|
||||
data = append(data, new(int))
|
||||
case Rune:
|
||||
data = append(data, new(rune))
|
||||
case String:
|
||||
data = append(data, new(string))
|
||||
case URI:
|
||||
data = append(data, new(string))
|
||||
}
|
||||
}
|
||||
|
||||
count, err := fmt.Sscanf(str, s.format, data...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if count != len(data) {
|
||||
return fmt.Errorf("impossible to decode more than %v parameters in '%s' with format '%s'", count, str, s.format)
|
||||
}
|
||||
|
||||
for i, value := range s.source {
|
||||
switch x := value.(type) {
|
||||
case Bool:
|
||||
v := data[i].(*bool)
|
||||
|
||||
err := x.Set(*v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case Bytes:
|
||||
return fmt.Errorf("impossible to convert '%s' to []bytes type", str)
|
||||
case Float:
|
||||
v := data[i].(*float64)
|
||||
|
||||
err := x.Set(*v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case Int:
|
||||
v := data[i].(*int)
|
||||
|
||||
err := x.Set(*v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case Rune:
|
||||
v := data[i].(*rune)
|
||||
|
||||
err := x.Set(*v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case String:
|
||||
v := data[i].(*string)
|
||||
|
||||
err := x.Set(*v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case URI:
|
||||
v := data[i].(*string)
|
||||
|
||||
if v == nil {
|
||||
return fmt.Errorf("URI can not be nil in '%s'", str)
|
||||
}
|
||||
|
||||
uri, err := storage.ParseURI(*v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = x.Set(uri)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StringToStringWithFormat creates a binding that converts a string to another string using the specified format.
|
||||
// Changes to the returned String will be pushed to the passed in String and setting a new string value will parse and
|
||||
// set the underlying String if it matches the format and the parse was successful.
|
||||
//
|
||||
// Since: 2.2
|
||||
func StringToStringWithFormat(str String, format string) String {
|
||||
if format == "%s" { // Same as not using custom formatting.
|
||||
return str
|
||||
}
|
||||
|
||||
return NewSprintf(format, str)
|
||||
}
|
92
vendor/fyne.io/fyne/v2/data/binding/treebinding.go
generated
vendored
Normal file
92
vendor/fyne.io/fyne/v2/data/binding/treebinding.go
generated
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
package binding
|
||||
|
||||
// DataTreeRootID const is the value used as ID for the root of any tree binding.
|
||||
const DataTreeRootID = ""
|
||||
|
||||
// DataTree is the base interface for all bindable data trees.
|
||||
//
|
||||
// Since: 2.4
|
||||
type DataTree interface {
|
||||
DataItem
|
||||
GetItem(id string) (DataItem, error)
|
||||
ChildIDs(string) []string
|
||||
}
|
||||
|
||||
type treeBase struct {
|
||||
base
|
||||
|
||||
ids map[string][]string
|
||||
items map[string]DataItem
|
||||
}
|
||||
|
||||
// GetItem returns the DataItem at the specified id.
|
||||
func (t *treeBase) GetItem(id string) (DataItem, error) {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
if item, ok := t.items[id]; ok {
|
||||
return item, nil
|
||||
}
|
||||
|
||||
return nil, errOutOfBounds
|
||||
}
|
||||
|
||||
// ChildIDs returns the ordered IDs of items in this data tree that are children of the specified ID.
|
||||
func (t *treeBase) ChildIDs(id string) []string {
|
||||
t.lock.RLock()
|
||||
defer t.lock.RUnlock()
|
||||
|
||||
if ids, ok := t.ids[id]; ok {
|
||||
return ids
|
||||
}
|
||||
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (t *treeBase) appendItem(i DataItem, id, parent string) {
|
||||
t.items[id] = i
|
||||
ids, ok := t.ids[parent]
|
||||
if !ok {
|
||||
ids = make([]string, 0)
|
||||
}
|
||||
|
||||
for _, in := range ids {
|
||||
if in == id {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.ids[parent] = append(ids, id)
|
||||
}
|
||||
|
||||
func (t *treeBase) deleteItem(id, parent string) {
|
||||
delete(t.items, id)
|
||||
|
||||
ids, ok := t.ids[parent]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
off := -1
|
||||
for i, id2 := range ids {
|
||||
if id2 == id {
|
||||
off = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if off == -1 {
|
||||
return
|
||||
}
|
||||
t.ids[parent] = append(ids[:off], ids[off+1:]...)
|
||||
}
|
||||
|
||||
func parentIDFor(id string, ids map[string][]string) string {
|
||||
for parent, list := range ids {
|
||||
for _, child := range list {
|
||||
if child == id {
|
||||
return parent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
39
vendor/fyne.io/fyne/v2/device.go
generated
vendored
Normal file
39
vendor/fyne.io/fyne/v2/device.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package fyne
|
||||
|
||||
// DeviceOrientation represents the different ways that a mobile device can be held
|
||||
type DeviceOrientation int
|
||||
|
||||
const (
|
||||
// OrientationVertical is the default vertical orientation
|
||||
OrientationVertical DeviceOrientation = iota
|
||||
// OrientationVerticalUpsideDown is the portrait orientation held upside down
|
||||
OrientationVerticalUpsideDown
|
||||
// OrientationHorizontalLeft is used to indicate a landscape orientation with the top to the left
|
||||
OrientationHorizontalLeft
|
||||
// OrientationHorizontalRight is used to indicate a landscape orientation with the top to the right
|
||||
OrientationHorizontalRight
|
||||
)
|
||||
|
||||
// IsVertical is a helper utility that determines if a passed orientation is vertical
|
||||
func IsVertical(orient DeviceOrientation) bool {
|
||||
return orient == OrientationVertical || orient == OrientationVerticalUpsideDown
|
||||
}
|
||||
|
||||
// IsHorizontal is a helper utility that determines if a passed orientation is horizontal
|
||||
func IsHorizontal(orient DeviceOrientation) bool {
|
||||
return !IsVertical(orient)
|
||||
}
|
||||
|
||||
// Device provides information about the devices the code is running on
|
||||
type Device interface {
|
||||
Orientation() DeviceOrientation
|
||||
IsMobile() bool
|
||||
IsBrowser() bool
|
||||
HasKeyboard() bool
|
||||
SystemScaleForWindow(Window) float32
|
||||
}
|
||||
|
||||
// CurrentDevice returns the device information for the current hardware (via the driver)
|
||||
func CurrentDevice() Device {
|
||||
return CurrentApp().Driver().Device()
|
||||
}
|
32
vendor/fyne.io/fyne/v2/driver.go
generated
vendored
Normal file
32
vendor/fyne.io/fyne/v2/driver.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package fyne
|
||||
|
||||
// Driver defines an abstract concept of a Fyne render driver.
|
||||
// Any implementation must provide at least these methods.
|
||||
type Driver interface {
|
||||
// CreateWindow creates a new UI Window.
|
||||
CreateWindow(string) Window
|
||||
// AllWindows returns a slice containing all app windows.
|
||||
AllWindows() []Window
|
||||
|
||||
// RenderedTextSize returns the size required to render the given string of specified
|
||||
// font size and style. It also returns the height to text baseline, measured from the top.
|
||||
RenderedTextSize(text string, fontSize float32, style TextStyle) (size Size, baseline float32)
|
||||
|
||||
// CanvasForObject returns the canvas that is associated with a given CanvasObject.
|
||||
CanvasForObject(CanvasObject) Canvas
|
||||
// AbsolutePositionForObject returns the position of a given CanvasObject relative to the top/left of a canvas.
|
||||
AbsolutePositionForObject(CanvasObject) Position
|
||||
|
||||
// Device returns the device that the application is currently running on.
|
||||
Device() Device
|
||||
// Run starts the main event loop of the driver.
|
||||
Run()
|
||||
// Quit closes the driver and open windows, then exit the application.
|
||||
// On some some operating systems this does nothing, for example iOS and Android.
|
||||
Quit()
|
||||
|
||||
// StartAnimation registers a new animation with this driver and requests it be started.
|
||||
StartAnimation(*Animation)
|
||||
// StopAnimation stops an animation and unregisters from this driver.
|
||||
StopAnimation(*Animation)
|
||||
}
|
11
vendor/fyne.io/fyne/v2/driver/desktop/app.go
generated
vendored
Normal file
11
vendor/fyne.io/fyne/v2/driver/desktop/app.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package desktop
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
// App defines the desktop specific extensions to a fyne.App.
|
||||
//
|
||||
// Since: 2.2
|
||||
type App interface {
|
||||
SetSystemTrayMenu(menu *fyne.Menu)
|
||||
SetSystemTrayIcon(icon fyne.Resource)
|
||||
}
|
11
vendor/fyne.io/fyne/v2/driver/desktop/canvas.go
generated
vendored
Normal file
11
vendor/fyne.io/fyne/v2/driver/desktop/canvas.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
package desktop
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
// Canvas defines the desktop specific extensions to a fyne.Canvas.
|
||||
type Canvas interface {
|
||||
OnKeyDown() func(*fyne.KeyEvent)
|
||||
SetOnKeyDown(func(*fyne.KeyEvent))
|
||||
OnKeyUp() func(*fyne.KeyEvent)
|
||||
SetOnKeyUp(func(*fyne.KeyEvent))
|
||||
}
|
47
vendor/fyne.io/fyne/v2/driver/desktop/cursor.go
generated
vendored
Normal file
47
vendor/fyne.io/fyne/v2/driver/desktop/cursor.go
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
package desktop
|
||||
|
||||
import "image"
|
||||
|
||||
// Cursor interface is used for objects that desire a specific cursor.
|
||||
//
|
||||
// Since: 2.0
|
||||
type Cursor interface {
|
||||
// Image returns the image for the given cursor, or nil if none should be shown.
|
||||
// It also returns the x and y pixels that should act as the hot-spot (measured from top left corner).
|
||||
Image() (image.Image, int, int)
|
||||
}
|
||||
|
||||
// StandardCursor represents a standard Fyne cursor.
|
||||
// These values were previously of type `fyne.Cursor`.
|
||||
//
|
||||
// Since: 2.0
|
||||
type StandardCursor int
|
||||
|
||||
// Image is not used for any of the StandardCursor types.
|
||||
//
|
||||
// Since: 2.0
|
||||
func (d StandardCursor) Image() (image.Image, int, int) {
|
||||
return nil, 0, 0
|
||||
}
|
||||
|
||||
const (
|
||||
// DefaultCursor is the default cursor typically an arrow
|
||||
DefaultCursor StandardCursor = iota
|
||||
// TextCursor is the cursor often used to indicate text selection
|
||||
TextCursor
|
||||
// CrosshairCursor is the cursor often used to indicate bitmaps
|
||||
CrosshairCursor
|
||||
// PointerCursor is the cursor often used to indicate a link
|
||||
PointerCursor
|
||||
// HResizeCursor is the cursor often used to indicate horizontal resize
|
||||
HResizeCursor
|
||||
// VResizeCursor is the cursor often used to indicate vertical resize
|
||||
VResizeCursor
|
||||
// HiddenCursor will cause the cursor to not be shown
|
||||
HiddenCursor
|
||||
)
|
||||
|
||||
// Cursorable describes any CanvasObject that needs a cursor change
|
||||
type Cursorable interface {
|
||||
Cursor() Cursor
|
||||
}
|
15
vendor/fyne.io/fyne/v2/driver/desktop/driver.go
generated
vendored
Normal file
15
vendor/fyne.io/fyne/v2/driver/desktop/driver.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// Package desktop provides desktop specific driver functionality.
|
||||
package desktop
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
// Driver represents the extended capabilities of a desktop driver
|
||||
type Driver interface {
|
||||
// Create a new borderless window that is centered on screen
|
||||
CreateSplashWindow() fyne.Window
|
||||
|
||||
// Gets the set of key modifiers that are currently active
|
||||
//
|
||||
// Since: 2.4
|
||||
CurrentKeyModifiers() fyne.KeyModifier
|
||||
}
|
66
vendor/fyne.io/fyne/v2/driver/desktop/key.go
generated
vendored
Normal file
66
vendor/fyne.io/fyne/v2/driver/desktop/key.go
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package desktop
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeyNone represents no key
|
||||
KeyNone fyne.KeyName = ""
|
||||
// KeyShiftLeft represents the left shift key
|
||||
KeyShiftLeft fyne.KeyName = "LeftShift"
|
||||
// KeyShiftRight represents the right shift key
|
||||
KeyShiftRight fyne.KeyName = "RightShift"
|
||||
// KeyControlLeft represents the left control key
|
||||
KeyControlLeft fyne.KeyName = "LeftControl"
|
||||
// KeyControlRight represents the right control key
|
||||
KeyControlRight fyne.KeyName = "RightControl"
|
||||
// KeyAltLeft represents the left alt key
|
||||
KeyAltLeft fyne.KeyName = "LeftAlt"
|
||||
// KeyAltRight represents the right alt key
|
||||
KeyAltRight fyne.KeyName = "RightAlt"
|
||||
// KeySuperLeft represents the left "Windows" key (or "Command" key on macOS)
|
||||
KeySuperLeft fyne.KeyName = "LeftSuper"
|
||||
// KeySuperRight represents the right "Windows" key (or "Command" key on macOS)
|
||||
KeySuperRight fyne.KeyName = "RightSuper"
|
||||
// KeyMenu represents the left or right menu / application key
|
||||
KeyMenu fyne.KeyName = "Menu"
|
||||
// KeyPrintScreen represents the key used to cause a screen capture
|
||||
KeyPrintScreen fyne.KeyName = "PrintScreen"
|
||||
|
||||
// KeyCapsLock represents the caps lock key, tapping once is the down event then again is the up
|
||||
KeyCapsLock fyne.KeyName = "CapsLock"
|
||||
)
|
||||
|
||||
// Modifier captures any key modifiers (shift etc.) pressed during a key event
|
||||
//
|
||||
// Deprecated: Use fyne.KeyModifier instead.
|
||||
type Modifier = fyne.KeyModifier
|
||||
|
||||
const (
|
||||
// ShiftModifier represents a shift key being held
|
||||
//
|
||||
// Deprecated: Use fyne.KeyModifierShift instead.
|
||||
ShiftModifier = fyne.KeyModifierShift
|
||||
// ControlModifier represents the ctrl key being held
|
||||
//
|
||||
// Deprecated: Use fyne.KeyModifierControl instead.
|
||||
ControlModifier = fyne.KeyModifierControl
|
||||
// AltModifier represents either alt keys being held
|
||||
//
|
||||
// Deprecated: Use fyne.KeyModifierAlt instead.
|
||||
AltModifier = fyne.KeyModifierAlt
|
||||
// SuperModifier represents either super keys being held
|
||||
//
|
||||
// Deprecated: Use fyne.KeyModifierSuper instead.
|
||||
SuperModifier = fyne.KeyModifierSuper
|
||||
)
|
||||
|
||||
// Keyable describes any focusable canvas object that can accept desktop key events.
|
||||
// This is the traditional key down and up event that is not applicable to all devices.
|
||||
type Keyable interface {
|
||||
fyne.Focusable
|
||||
|
||||
KeyDown(*fyne.KeyEvent)
|
||||
KeyUp(*fyne.KeyEvent)
|
||||
}
|
58
vendor/fyne.io/fyne/v2/driver/desktop/mouse.go
generated
vendored
Normal file
58
vendor/fyne.io/fyne/v2/driver/desktop/mouse.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
package desktop
|
||||
|
||||
import "fyne.io/fyne/v2"
|
||||
|
||||
// MouseButton represents a single button in a desktop MouseEvent
|
||||
type MouseButton int
|
||||
|
||||
const (
|
||||
// MouseButtonPrimary is the most common mouse button - on some systems the only one.
|
||||
// This will normally be on the left side of a mouse.
|
||||
//
|
||||
// Since: 2.0
|
||||
MouseButtonPrimary MouseButton = 1 << iota
|
||||
|
||||
// MouseButtonSecondary is the secondary button on most mouse input devices.
|
||||
// This will normally be on the right side of a mouse.
|
||||
//
|
||||
// Since: 2.0
|
||||
MouseButtonSecondary
|
||||
|
||||
// MouseButtonTertiary is the middle button on the mouse, assuming it has one.
|
||||
//
|
||||
// Since: 2.0
|
||||
MouseButtonTertiary
|
||||
|
||||
// LeftMouseButton is the most common mouse button - on some systems the only one.
|
||||
//
|
||||
// Deprecated: use MouseButtonPrimary which will adapt to mouse configuration.
|
||||
LeftMouseButton = MouseButtonPrimary
|
||||
|
||||
// RightMouseButton is the secondary button on most mouse input devices.
|
||||
//
|
||||
// Deprecated: use MouseButtonSecondary which will adapt to mouse configuration.
|
||||
RightMouseButton = MouseButtonSecondary
|
||||
)
|
||||
|
||||
// MouseEvent contains data relating to desktop mouse events
|
||||
type MouseEvent struct {
|
||||
fyne.PointEvent
|
||||
Button MouseButton
|
||||
Modifier fyne.KeyModifier
|
||||
}
|
||||
|
||||
// Mouseable represents desktop mouse events that can be sent to CanvasObjects
|
||||
type Mouseable interface {
|
||||
MouseDown(*MouseEvent)
|
||||
MouseUp(*MouseEvent)
|
||||
}
|
||||
|
||||
// Hoverable is used when a canvas object wishes to know if a pointer device moves over it.
|
||||
type Hoverable interface {
|
||||
// MouseIn is a hook that is called if the mouse pointer enters the element.
|
||||
MouseIn(*MouseEvent)
|
||||
// MouseMoved is a hook that is called if the mouse pointer moved over the element.
|
||||
MouseMoved(*MouseEvent)
|
||||
// MouseOut is a hook that is called if the mouse pointer leaves the element.
|
||||
MouseOut()
|
||||
}
|
61
vendor/fyne.io/fyne/v2/driver/desktop/shortcut.go
generated
vendored
Normal file
61
vendor/fyne.io/fyne/v2/driver/desktop/shortcut.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package desktop
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
// Declare conformity with Shortcut interface
|
||||
var _ fyne.Shortcut = (*CustomShortcut)(nil)
|
||||
var _ fyne.KeyboardShortcut = (*CustomShortcut)(nil)
|
||||
|
||||
// CustomShortcut describes a shortcut desktop event.
|
||||
type CustomShortcut struct {
|
||||
fyne.KeyName
|
||||
Modifier fyne.KeyModifier
|
||||
}
|
||||
|
||||
// Key returns the key name of this shortcut.
|
||||
// @implements KeyboardShortcut
|
||||
func (cs *CustomShortcut) Key() fyne.KeyName {
|
||||
return cs.KeyName
|
||||
}
|
||||
|
||||
// Mod returns the modifier of this shortcut.
|
||||
// @implements KeyboardShortcut
|
||||
func (cs *CustomShortcut) Mod() fyne.KeyModifier {
|
||||
return cs.Modifier
|
||||
}
|
||||
|
||||
// ShortcutName returns the shortcut name associated to the event
|
||||
func (cs *CustomShortcut) ShortcutName() string {
|
||||
id := &strings.Builder{}
|
||||
id.WriteString("CustomDesktop:")
|
||||
id.WriteString(modifierToString(cs.Modifier))
|
||||
id.WriteString("+")
|
||||
id.WriteString(string(cs.KeyName))
|
||||
return id.String()
|
||||
}
|
||||
|
||||
func modifierToString(mods fyne.KeyModifier) string {
|
||||
s := []string{}
|
||||
if (mods & fyne.KeyModifierShift) != 0 {
|
||||
s = append(s, string("Shift"))
|
||||
}
|
||||
if (mods & fyne.KeyModifierControl) != 0 {
|
||||
s = append(s, string("Control"))
|
||||
}
|
||||
if (mods & fyne.KeyModifierAlt) != 0 {
|
||||
s = append(s, string("Alt"))
|
||||
}
|
||||
if (mods & fyne.KeyModifierSuper) != 0 {
|
||||
if runtime.GOOS == "darwin" {
|
||||
s = append(s, string("Command"))
|
||||
} else {
|
||||
s = append(s, string("Super"))
|
||||
}
|
||||
}
|
||||
return strings.Join(s, "+")
|
||||
}
|
12
vendor/fyne.io/fyne/v2/driver/mobile/device.go
generated
vendored
Normal file
12
vendor/fyne.io/fyne/v2/driver/mobile/device.go
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
// Package mobile provides mobile specific driver functionality.
|
||||
package mobile
|
||||
|
||||
// Device describes functionality only available on mobile
|
||||
type Device interface {
|
||||
// Request that the mobile device show the touch screen keyboard (standard layout)
|
||||
ShowVirtualKeyboard()
|
||||
// Request that the mobile device show the touch screen keyboard (custom layout)
|
||||
ShowVirtualKeyboardType(KeyboardType)
|
||||
// Request that the mobile device dismiss the touch screen keyboard
|
||||
HideVirtualKeyboard()
|
||||
}
|
10
vendor/fyne.io/fyne/v2/driver/mobile/driver.go
generated
vendored
Normal file
10
vendor/fyne.io/fyne/v2/driver/mobile/driver.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
// Package mobile provides desktop specific mobile functionality.
|
||||
package mobile
|
||||
|
||||
// Driver represents the extended capabilities of a mobile driver
|
||||
//
|
||||
// Since: 2.4
|
||||
type Driver interface {
|
||||
// GoBack asks the OS to go to the previous app / activity, where supported
|
||||
GoBack()
|
||||
}
|
10
vendor/fyne.io/fyne/v2/driver/mobile/key.go
generated
vendored
Normal file
10
vendor/fyne.io/fyne/v2/driver/mobile/key.go
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package mobile
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeyBack represents the back button which may be hardware or software
|
||||
KeyBack fyne.KeyName = "Back"
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user