The very basic client app, not adapted for mobiles.
This commit is contained in:
54
client/internal/services/core/mainwindow/about_dialog.go
Normal file
54
client/internal/services/core/mainwindow/about_dialog.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/canvas"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/dialog"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
"fyne.io/fyne/v2/theme"
|
||||
)
|
||||
|
||||
const (
|
||||
minRowsVisibleInSysInfo = 10
|
||||
startYear = 2025
|
||||
)
|
||||
|
||||
func (m *mainWindow) showAboutDialog() {
|
||||
appNameLbl := canvas.NewText(
|
||||
"pztrn's Bunker",
|
||||
m.app.Fyne().Settings().Theme().Color(
|
||||
theme.ColorNameForeground,
|
||||
m.app.Fyne().Settings().ThemeVariant(),
|
||||
),
|
||||
)
|
||||
appNameLbl.TextSize = 16
|
||||
appNameLbl.TextStyle = fyne.TextStyle{Bold: true}
|
||||
appNameLbl.Alignment = fyne.TextAlignCenter
|
||||
|
||||
appVersionLbl := canvas.NewText(
|
||||
lang.L("about_dialog.version")+" "+m.app.Fyne().Metadata().Custom["Version"],
|
||||
m.app.Fyne().Settings().Theme().Color(
|
||||
theme.ColorNameForeground,
|
||||
m.app.Fyne().Settings().ThemeVariant(),
|
||||
),
|
||||
)
|
||||
appVersionLbl.TextSize = 16
|
||||
appVersionLbl.TextStyle = fyne.TextStyle{Bold: true}
|
||||
appVersionLbl.Alignment = fyne.TextAlignCenter
|
||||
|
||||
tabs := container.NewAppTabs(
|
||||
m.generateAboutTab(),
|
||||
m.generateLicensesTab(),
|
||||
m.generateSysInfoTab(),
|
||||
)
|
||||
|
||||
vbox := container.NewVBox(
|
||||
appNameLbl,
|
||||
appVersionLbl,
|
||||
tabs,
|
||||
)
|
||||
|
||||
dlg := dialog.NewCustom(lang.L("about_dialog.title"), lang.L("about_dialog.close_button"), vbox, m.window)
|
||||
dlg.Show()
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
func (m *mainWindow) generateAboutTab() *container.TabItem {
|
||||
copyrightYears := strconv.Itoa(startYear)
|
||||
if time.Now().Year() > startYear {
|
||||
copyrightYears = strconv.Itoa(startYear) + " - " + strconv.Itoa(time.Now().Year())
|
||||
}
|
||||
|
||||
aboutLabel := widget.NewLabel(
|
||||
lang.L("about_dialog.about_tab.summary") +
|
||||
"\n\n" +
|
||||
lang.L("about_dialog.about_tab.copyright", map[string]any{"Years": copyrightYears}),
|
||||
)
|
||||
aboutLabel.Wrapping = fyne.TextWrapWord
|
||||
|
||||
aboutVBox := container.NewVBox(aboutLabel)
|
||||
aboutScroll := container.NewVScroll(aboutVBox)
|
||||
|
||||
aboutScroll.SetMinSize(fyne.NewSize(
|
||||
m.window.Content().Size().Width-m.window.Content().Size().Width/3,
|
||||
m.window.Content().Size().Height-m.window.Content().Size().Height/2.5,
|
||||
))
|
||||
|
||||
return container.NewTabItem(lang.L("about_dialog.about_tab"), aboutScroll)
|
||||
}
|
@@ -0,0 +1,293 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
func (m *mainWindow) generateLicensesTab() *container.TabItem {
|
||||
licensesLabel := widget.NewLabel(lang.L("about_dialog.licenses_tab.summary"))
|
||||
licensesLabel.Wrapping = fyne.TextWrapWord
|
||||
|
||||
accordion := widget.NewAccordion(
|
||||
m.generateFyneAccordionItem(),
|
||||
m.generateGooseAccordionItem(),
|
||||
m.generateGopsutilAccordionItem(),
|
||||
m.generateModerncSqliteAccordionItem(),
|
||||
m.generateSqlxAccrodionItem(),
|
||||
)
|
||||
|
||||
licensesVBox := container.NewVBox(licensesLabel, accordion)
|
||||
licensesScroll := container.NewVScroll(licensesVBox)
|
||||
|
||||
return container.NewTabItem(lang.L("about_dialog.licenses_tab"), licensesScroll)
|
||||
}
|
||||
|
||||
func (m *mainWindow) generateFyneAccordionItem() *widget.AccordionItem {
|
||||
summary := widget.NewLabel(lang.L("about_dialog.licenses_tab.dependency.fyne"))
|
||||
summary.Wrapping = fyne.TextWrapWord
|
||||
|
||||
license := widget.NewLabel(`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.`)
|
||||
license.TextStyle = fyne.TextStyle{Monospace: true}
|
||||
license.Wrapping = fyne.TextWrapWord
|
||||
|
||||
url, _ := url.Parse("https://github.com/fyne-io/fyne")
|
||||
authorsURL, _ := url.Parse("https://raw.githubusercontent.com/fyne-io/fyne/refs/heads/master/AUTHORS")
|
||||
|
||||
return widget.NewAccordionItem("Fyne v2.6.1", container.NewVBox(
|
||||
summary,
|
||||
container.NewHBox(
|
||||
widget.NewHyperlink("Source", url),
|
||||
widget.NewLabel("|"),
|
||||
widget.NewHyperlink("Authors", authorsURL),
|
||||
),
|
||||
license,
|
||||
))
|
||||
}
|
||||
|
||||
func (m *mainWindow) generateGooseAccordionItem() *widget.AccordionItem {
|
||||
summary := widget.NewLabel(lang.L("about_dialog.licenses_tab.dependency.goose"))
|
||||
summary.Wrapping = fyne.TextWrapWord
|
||||
|
||||
license := widget.NewLabel(`MIT License
|
||||
|
||||
Original work Copyright (c) 2012 Liam Staskawicz
|
||||
Modified work Copyright (c) 2016 Vojtech Vitek
|
||||
Modified work Copyright (c) 2021 Michael Fridman, Vojtech Vitek
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.`)
|
||||
license.TextStyle = fyne.TextStyle{Monospace: true}
|
||||
license.Wrapping = fyne.TextWrapWord
|
||||
|
||||
url, _ := url.Parse("https://github.com/pressly/goose")
|
||||
authorsURL, _ := url.Parse("https://github.com/pressly/goose/graphs/contributors")
|
||||
|
||||
return widget.NewAccordionItem("goose v3.24.3", container.NewVBox(
|
||||
summary,
|
||||
container.NewHBox(
|
||||
widget.NewHyperlink("Source", url),
|
||||
widget.NewLabel("|"),
|
||||
widget.NewHyperlink("Authors", authorsURL),
|
||||
),
|
||||
license,
|
||||
))
|
||||
}
|
||||
|
||||
func (m *mainWindow) generateGopsutilAccordionItem() *widget.AccordionItem {
|
||||
summary := widget.NewLabel(lang.L("about_dialog.licenses_tab.dependency.gopsutil"))
|
||||
summary.Wrapping = fyne.TextWrapWord
|
||||
|
||||
license := widget.NewLabel(`gopsutil is distributed under BSD license reproduced below.
|
||||
|
||||
Copyright (c) 2014, WAKAYAMA Shirou
|
||||
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 the gopsutil authors 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 OWNER OR CONTRIBUTORS 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.
|
||||
|
||||
|
||||
-------
|
||||
internal/common/binary.go in the gopsutil is copied and modified from golang/encoding/binary.go.
|
||||
|
||||
|
||||
|
||||
Copyright (c) 2009 The Go 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 Google Inc. 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
|
||||
OWNER OR CONTRIBUTORS 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.`)
|
||||
license.TextStyle = fyne.TextStyle{Monospace: true}
|
||||
license.Wrapping = fyne.TextWrapWord
|
||||
|
||||
url, _ := url.Parse("https://github.com/shirou/gopsutil")
|
||||
authorsURL, _ := url.Parse("https://github.com/shirou/gopsutil/graphs/contributors")
|
||||
|
||||
return widget.NewAccordionItem("gopsutil v3.24.5", container.NewVBox(
|
||||
summary,
|
||||
container.NewHBox(
|
||||
widget.NewHyperlink("Source", url),
|
||||
widget.NewLabel("|"),
|
||||
widget.NewHyperlink("Authors", authorsURL),
|
||||
),
|
||||
license,
|
||||
))
|
||||
}
|
||||
|
||||
func (m *mainWindow) generateModerncSqliteAccordionItem() *widget.AccordionItem {
|
||||
summary := widget.NewLabel(lang.L("about_dialog.licenses_tab.dependency.modernc_sqlite"))
|
||||
summary.Wrapping = fyne.TextWrapWord
|
||||
|
||||
license := widget.NewLabel(`Copyright (c) 2017 The Sqlite 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:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder 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 OR CONTRIBUTORS 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.
|
||||
|
||||
`)
|
||||
license.TextStyle = fyne.TextStyle{Monospace: true}
|
||||
license.Wrapping = fyne.TextWrapWord
|
||||
|
||||
url, _ := url.Parse("https://gitlab.com/cznic/sqlite")
|
||||
authorsURL, _ := url.Parse("https://gitlab.com/cznic/sqlite/-/raw/master/AUTHORS")
|
||||
|
||||
return widget.NewAccordionItem("modernc/sqlite v1.37.1", container.NewVBox(
|
||||
summary,
|
||||
container.NewHBox(
|
||||
widget.NewHyperlink("Source", url),
|
||||
widget.NewLabel("|"),
|
||||
widget.NewHyperlink("Authors", authorsURL),
|
||||
),
|
||||
license,
|
||||
))
|
||||
}
|
||||
|
||||
func (m *mainWindow) generateSqlxAccrodionItem() *widget.AccordionItem {
|
||||
summary := widget.NewLabel(lang.L("about_dialog.licenses_tab.dependency.sqlx"))
|
||||
summary.Wrapping = fyne.TextWrapWord
|
||||
|
||||
license := widget.NewLabel(`Copyright (c) 2013, Jason Moiron
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.`)
|
||||
license.TextStyle = fyne.TextStyle{Monospace: true}
|
||||
license.Wrapping = fyne.TextWrapWord
|
||||
|
||||
url, _ := url.Parse("https://github.com/jmoiron/sqlx")
|
||||
authorsURL, _ := url.Parse("https://github.com/jmoiron/sqlx/graphs/contributors")
|
||||
|
||||
return widget.NewAccordionItem("sqlx v1.4.0", container.NewVBox(
|
||||
summary,
|
||||
container.NewHBox(
|
||||
widget.NewHyperlink("Source", url),
|
||||
widget.NewLabel("|"),
|
||||
widget.NewHyperlink("Authors", authorsURL),
|
||||
),
|
||||
license,
|
||||
))
|
||||
}
|
@@ -0,0 +1,221 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
|
||||
"bunker/client/internal/services/core"
|
||||
"bunker/client/internal/services/core/mainwindow/models"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
"github.com/shirou/gopsutil/v3/host"
|
||||
"github.com/shirou/gopsutil/v3/mem"
|
||||
"github.com/shirou/gopsutil/v3/process"
|
||||
)
|
||||
|
||||
var errSysInfoHandlerAlreadyRegistered = errors.New("handler already registered")
|
||||
|
||||
func (m *mainWindow) generateSysInfoTab() *container.TabItem {
|
||||
lbl := widget.NewLabel(lang.L("about_dialog.sysinfo_tab.summary"))
|
||||
lbl.Wrapping = fyne.TextWrapWord
|
||||
|
||||
buildInfo, _ := debug.ReadBuildInfo()
|
||||
|
||||
var builtForOS, builtForArch string
|
||||
|
||||
for _, bi := range buildInfo.Settings {
|
||||
switch bi.Key {
|
||||
case "GOARCH":
|
||||
builtForArch = bi.Value
|
||||
case "GOOS":
|
||||
builtForOS = bi.Value
|
||||
}
|
||||
}
|
||||
|
||||
commons, _ := host.Info()
|
||||
|
||||
cpuInfo, _ := cpu.Info()
|
||||
|
||||
memInfo, _ := mem.VirtualMemory()
|
||||
|
||||
memoryTotal := memInfo.Total
|
||||
memoryTotalDivCount := 0
|
||||
|
||||
var memoryTotalString string
|
||||
|
||||
for {
|
||||
//nolint:mnd
|
||||
if memoryTotal > 1024 {
|
||||
memoryTotal = memoryTotal / 1024
|
||||
memoryTotalDivCount++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
//nolint:mnd
|
||||
switch memoryTotalDivCount {
|
||||
case 1:
|
||||
memoryTotalString = fmt.Sprintf("%d KB", memoryTotal)
|
||||
case 2:
|
||||
memoryTotalString = fmt.Sprintf("%d MB", memoryTotal)
|
||||
default:
|
||||
memoryTotalString = fmt.Sprintf("%d GB", memoryTotal)
|
||||
}
|
||||
|
||||
memoryFree := memInfo.Available
|
||||
memoryFreeDivCount := 0
|
||||
|
||||
var memoryFreeString string
|
||||
|
||||
//nolint:mnd
|
||||
for {
|
||||
if memoryFree > 1024 {
|
||||
memoryFree = memoryFree / 1024
|
||||
memoryFreeDivCount++
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
//nolint:mnd
|
||||
switch memoryFreeDivCount {
|
||||
case 1:
|
||||
memoryFreeString = fmt.Sprintf("%d KB", memoryFree)
|
||||
case 2:
|
||||
memoryFreeString = fmt.Sprintf("%d MB", memoryFree)
|
||||
default:
|
||||
memoryFreeString = fmt.Sprintf("%d GB", memoryFree)
|
||||
}
|
||||
|
||||
var (
|
||||
conns int
|
||||
fds int32
|
||||
)
|
||||
|
||||
//nolint:gosec
|
||||
proc, err := process.NewProcess(int32(os.Getpid()))
|
||||
if err == nil {
|
||||
connections, _ := proc.Connections()
|
||||
conns = len(connections)
|
||||
|
||||
fds, _ = proc.NumFDs()
|
||||
}
|
||||
|
||||
sysInfoData := fmt.Sprintf(`## Build information
|
||||
|
||||
- Version: %s (#%s, from %s/%s, on %s)
|
||||
- Built for: %s/%s
|
||||
|
||||
## System information
|
||||
|
||||
- CPU: %s (%d cores)
|
||||
- RAM: %s (%s available)
|
||||
- OS: %s %s
|
||||
- Processes running: %d
|
||||
|
||||
## Launch information
|
||||
|
||||
- Running on: %s/%s
|
||||
- Opened network connections: %d
|
||||
- Opened file descriptors: %d
|
||||
|
||||
## Additional information
|
||||
|
||||
%s
|
||||
`,
|
||||
m.app.Fyne().Metadata().Custom["Version"],
|
||||
m.app.Fyne().Metadata().Custom["Build"],
|
||||
m.app.Fyne().Metadata().Custom["Branch"],
|
||||
m.app.Fyne().Metadata().Custom["Commit"],
|
||||
m.app.Fyne().Metadata().Custom["BuildDate"],
|
||||
builtForOS,
|
||||
builtForArch,
|
||||
cpuInfo[0].ModelName,
|
||||
cpuInfo[0].Cores,
|
||||
memoryTotalString,
|
||||
memoryFreeString,
|
||||
commons.OS,
|
||||
commons.PlatformVersion,
|
||||
commons.Procs,
|
||||
runtime.GOOS,
|
||||
runtime.GOARCH,
|
||||
conns,
|
||||
fds,
|
||||
m.prepareSysInfoAdditionalsForAbout(),
|
||||
)
|
||||
|
||||
sysInfo := widget.NewMultiLineEntry()
|
||||
sysInfo.OnChanged = func(_ string) {
|
||||
sysInfo.SetText(sysInfoData)
|
||||
}
|
||||
sysInfo.SetText(sysInfoData)
|
||||
sysInfo.SetMinRowsVisible(minRowsVisibleInSysInfo)
|
||||
|
||||
copyToClipboard := widget.NewButton("Copy to clipboard", func() {
|
||||
m.app.Fyne().Clipboard().SetContent(sysInfoData)
|
||||
|
||||
m.app.Fyne().SendNotification(&fyne.Notification{
|
||||
Title: lang.L("about_dialog.sysinfo_tab.copy_to_clipboard.notification.title"),
|
||||
Content: lang.L("about_dialog.sysinfo_tab.copy_to_clipboard.notification.content"),
|
||||
})
|
||||
})
|
||||
|
||||
sysInfoVBox := container.NewVBox(
|
||||
lbl,
|
||||
sysInfo,
|
||||
copyToClipboard,
|
||||
)
|
||||
|
||||
return container.NewTabItem(lang.L("about_dialog.sysinfo_tab"), sysInfoVBox)
|
||||
}
|
||||
|
||||
func (m *mainWindow) prepareSysInfoAdditionalsForAbout() string {
|
||||
additionals := ""
|
||||
|
||||
handlersNames := make([]string, 0, len(m.sysInfoHandlers))
|
||||
|
||||
for name := range m.sysInfoHandlers {
|
||||
handlersNames = append(handlersNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(handlersNames)
|
||||
|
||||
for _, name := range handlersNames {
|
||||
additionals += m.sysInfoHandlers[name].Handler() + "\n"
|
||||
}
|
||||
|
||||
return additionals
|
||||
}
|
||||
|
||||
func (m *mainWindow) RegisterAboutWindowSysInfoHandler(name string, hndl core.SysInfoHandler) error {
|
||||
if _, found := m.sysInfoHandlers[name]; found {
|
||||
return fmt.Errorf(
|
||||
"%w: RegisterAboutWindowSysInfoHandler: register '%s': %w",
|
||||
core.ErrMainWindow,
|
||||
name,
|
||||
errSysInfoHandlerAlreadyRegistered,
|
||||
)
|
||||
}
|
||||
|
||||
sysInfoHandler := &models.SysInfoHandler{
|
||||
Name: name,
|
||||
Handler: hndl,
|
||||
}
|
||||
|
||||
m.sysInfoHandlers[name] = sysInfoHandler
|
||||
|
||||
return nil
|
||||
}
|
109
client/internal/services/core/mainwindow/mainwindow.go
Normal file
109
client/internal/services/core/mainwindow/mainwindow.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"bunker/client/internal/application"
|
||||
"bunker/client/internal/services/core"
|
||||
"bunker/client/internal/services/core/mainwindow/models"
|
||||
"bunker/commons"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
|
||||
var _ = core.MainWindow(&mainWindow{})
|
||||
|
||||
type mainWindow struct {
|
||||
app *application.Application
|
||||
window fyne.Window
|
||||
options core.Options
|
||||
tabs *container.AppTabs
|
||||
sysInfoHandlers map[string]*models.SysInfoHandler
|
||||
}
|
||||
|
||||
// Initialize инициализирует сервис.
|
||||
func Initialize(app *application.Application) error {
|
||||
mainW := &mainWindow{
|
||||
app: app,
|
||||
}
|
||||
|
||||
if err := app.RegisterService(mainW); err != nil {
|
||||
return fmt.Errorf("%w: %w", core.ErrMainWindow, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mainWindow) Configure() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mainWindow) ConnectDependencies() error {
|
||||
optionsRaw := m.app.Service(core.ServiceNameOptions)
|
||||
if optionsRaw == nil {
|
||||
return fmt.Errorf("connect dependencies: get options service: %w", application.ErrServiceNotFound)
|
||||
}
|
||||
|
||||
options, valid := optionsRaw.(core.Options)
|
||||
if !valid {
|
||||
return fmt.Errorf("connect dependencies: type assert options service: %w", core.ErrOptionsIsInvalid)
|
||||
}
|
||||
|
||||
m.options = options
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mainWindow) Initialize() error {
|
||||
m.sysInfoHandlers = make(map[string]*models.SysInfoHandler)
|
||||
|
||||
m.window = m.app.Fyne().NewWindow(lang.L("window.title"))
|
||||
// ToDo: сохранение и восстановление размеров окна.
|
||||
//nolint:mnd
|
||||
m.window.Resize(fyne.NewSize(800, 650))
|
||||
|
||||
m.initializeMenu()
|
||||
|
||||
m.window.SetCloseIntercept(m.stopApp)
|
||||
|
||||
welcomeLabel := widget.NewLabel(lang.L("window.lorem_ipsum.text"))
|
||||
welcomeLabel.Wrapping = fyne.TextWrapWord
|
||||
|
||||
m.tabs = container.NewAppTabs(
|
||||
container.NewTabItem(lang.L("window.lorem_ipsum.tab_name"), welcomeLabel),
|
||||
)
|
||||
m.window.SetContent(m.tabs)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mainWindow) MainWindow() fyne.Window {
|
||||
return m.window
|
||||
}
|
||||
|
||||
func (m *mainWindow) Name() string {
|
||||
return core.ServiceNameMainWindow
|
||||
}
|
||||
|
||||
func (m *mainWindow) LaunchStartupTasks() error {
|
||||
m.window.Show()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mainWindow) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mainWindow) stopApp() {
|
||||
if err := m.app.Shutdown(); err != nil {
|
||||
slog.Error("Failed to stop Bunker!", "error", err.Error())
|
||||
|
||||
os.Exit(commons.ExitCodeAppStopFailed)
|
||||
}
|
||||
}
|
35
client/internal/services/core/mainwindow/mainwindow_menu.go
Normal file
35
client/internal/services/core/mainwindow/mainwindow_menu.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
)
|
||||
|
||||
func (m *mainWindow) initializeMenu() {
|
||||
optionsMenuItem := fyne.NewMenuItem(lang.L("main_menu.file.options_menu_item"), func() {
|
||||
slog.Info("Opening options...")
|
||||
|
||||
m.options.ShowOptionsDialog()
|
||||
})
|
||||
|
||||
exitMenuItem := fyne.NewMenuItem(lang.L("main_menu.file.exit_menu_item"), func() {
|
||||
m.stopApp()
|
||||
})
|
||||
exitMenuItem.IsQuit = true
|
||||
|
||||
fileMenu := fyne.NewMenu(lang.L("main_menu.file"), optionsMenuItem, fyne.NewMenuItemSeparator(), exitMenuItem)
|
||||
|
||||
aboutMenuItem := fyne.NewMenuItem(lang.L("main_menu.about.about_bunker_menu_item"), func() {
|
||||
m.showAboutDialog()
|
||||
})
|
||||
|
||||
aboutMenu := fyne.NewMenu(lang.L("main_menu.about"), aboutMenuItem)
|
||||
|
||||
mainMenu := fyne.NewMainMenu(
|
||||
fileMenu,
|
||||
aboutMenu,
|
||||
)
|
||||
m.window.SetMainMenu(mainMenu)
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package models
|
||||
|
||||
// SysInfoHandler содержит в себе информацию об обработчике для Markdown системной информации.
|
||||
type SysInfoHandler struct {
|
||||
Handler func() string
|
||||
Name string
|
||||
}
|
14
client/internal/services/core/mainwindow/tabs.go
Normal file
14
client/internal/services/core/mainwindow/tabs.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package mainwindow
|
||||
|
||||
import (
|
||||
"fyne.io/fyne/v2/container"
|
||||
"fyne.io/fyne/v2/lang"
|
||||
)
|
||||
|
||||
func (m *mainWindow) AddTab(tab *container.TabItem) {
|
||||
if len(m.tabs.Items) == 1 && m.tabs.Items[0].Text == lang.L("window.lorem_ipsum.tab_name") {
|
||||
m.tabs.Remove(m.tabs.Items[0])
|
||||
}
|
||||
|
||||
m.tabs.Append(tab)
|
||||
}
|
Reference in New Issue
Block a user