-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
262 lines (229 loc) · 7.68 KB
/
Copy pathmain.go
File metadata and controls
262 lines (229 loc) · 7.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
package main
import (
"fmt"
"image/color"
"net/url"
"os"
"os/exec"
"path/filepath"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
)
// TODO: check if new version available and provide instructions to update in a dialog.
func main() {
a := app.NewWithID("com.softorage.7gl")
a.SetIcon(resourceLogoPng)
w := a.NewWindow("7-Zip GUI for Linux")
w.Resize(fyne.NewSize(1040, 650))
// Bottom Info Bar
infoBar = widget.NewLabel("Ready. Interact with an option to see its description.")
infoBar.Alignment = fyne.TextAlignCenter
infoBar.Wrapping = fyne.TextWrapWord // Properly wraps text instead of resizing window
// Build Tab Contents
explorerTab := buildExplorerTab(w)
compressTab := buildCompressTab(w)
extractTab := buildExtractTab(w)
checksumTab := buildChecksumTab(w)
statusTab := buildStatusTab(w)
// Create a Max container that will act as the dynamic main content area
contentArea := container.NewMax()
// Construct Sidebar Tabs Menu
titles := make([]string, 5)
titles[ExplorerTabRank] = "Explorer"
titles[CompressTabRank] = "Compress"
titles[ExtractTabRank] = "Extract"
titles[ChecksumTabRank] = "Checksum"
titles[StatusTabRank] = "Status"
tabs = widget.NewList(
func() int { return len(titles) },
func() fyne.CanvasObject {
lbl := widget.NewLabel("")
lbl.TextStyle = fyne.TextStyle{Bold: true}
return container.NewPadded(lbl)
},
func(i widget.ListItemID, o fyne.CanvasObject) {
// Updating list elements safely
o.(*fyne.Container).Objects[0].(*widget.Label).SetText(titles[i])
},
)
// Handle switching tab views
tabs.OnSelected = func(id widget.ListItemID) {
stateMu.RLock()
running := isOperationRunning
stateMu.RUnlock()
if running && id != StatusTabRank {
setInfo("Action locked: Operation currently in progress.")
tabs.Select(StatusTabRank) // Force back to Status
return
}
// Swap out the objects inside the main content area
switch id {
case ExplorerTabRank:
contentArea.Objects = []fyne.CanvasObject{explorerTab}
case CompressTabRank:
contentArea.Objects = []fyne.CanvasObject{compressTab}
case ExtractTabRank:
contentArea.Objects = []fyne.CanvasObject{extractTab}
case ChecksumTabRank:
contentArea.Objects = []fyne.CanvasObject{checksumTab}
case StatusTabRank:
contentArea.Objects = []fyne.CanvasObject{statusTab}
}
contentArea.Refresh()
}
// Construct Sidebar Elements
// Top: App Name and version
appLabel := widget.NewRichText(
&widget.TextSegment{
Text: "7GL",
Style: widget.RichTextStyle{
SizeName: theme.SizeNameHeadingText,
TextStyle: fyne.TextStyle{Bold: true},
Alignment: fyne.TextAlignCenter,
},
},
&widget.TextSegment{
Text: fmt.Sprintf("\nversion %s", version),
Style: widget.RichTextStyle{
SizeName: theme.SizeNameCaptionText,
ColorName: theme.ColorNamePlaceHolder,
Alignment: fyne.TextAlignCenter,
},
},
)
// Center the text using an HBox with spacers on both sides
centeredAppLabel := container.NewHBox(layout.NewSpacer(), appLabel, layout.NewSpacer())
sidebarTop := container.NewVBox(
container.NewPadded(centeredAppLabel),
widget.NewLabel(""),
)
// Bottom: Github URL, Sponsor URL & Version
sourceCodeURL, _ := url.Parse("https://github.com/Softorage/7z-GUI-Linux")
sponsorURL, _ := url.Parse("https://rzp.io/rzp/hY39lZGa")
//sourceCodeBtn := widget.NewButtonWithIcon("View Source", resourceSourceCodeSvg, func() { a.OpenURL(sourceCodeURL) })
sourceCodeBtn := widget.NewButton("View Source ↗", func() { a.OpenURL(sourceCodeURL) })
//sourceCodeBtn.IconPlacement = widget.ButtonIconLeadingText
sourceCodeBtn.Importance = widget.LowImportance
sourceCodeBtn.Alignment = widget.ButtonAlignLeading
sponsorBtn := widget.NewButton("Sponsor ↗", func() { a.OpenURL(sponsorURL) })
sponsorBtn.Importance = widget.LowImportance
sponsorBtn.Alignment = widget.ButtonAlignLeading
tabsBottom := container.NewVBox(
container.NewPadded(sourceCodeBtn),
container.NewPadded(sponsorBtn),
)
aboutText := widget.NewRichText(&widget.TextSegment{
Text: fmt.Sprintf("A Softorage Project"),
Style: widget.RichTextStyle{
SizeName: theme.SizeNameCaptionText,
ColorName: theme.ColorNamePlaceHolder,
},
})
sidebarBottom := container.NewVBox(
tabsBottom,
container.NewCenter(aboutText),
)
sidebarContent := container.NewBorder(sidebarTop, sidebarBottom, nil, nil, tabs)
// Create Sidebar Background
// A translucent gray (alpha=25) creates a subtle contrast for both Light and Dark themes.
sidebarBg := canvas.NewRectangle(color.NRGBA{R: 128, G: 128, B: 128, A: 25})
// Force a minimum width to make the sidebar cozier/wider (180px width)
sidebarBg.SetMinSize(fyne.NewSize(180, 0))
// Combine the background color and the sidebar content
sidebar := container.NewMax(sidebarBg, sidebarContent)
// Combine Sidebar (Left) and Main Content (Right)
mainLayout := container.NewBorder(
nil,
nil,
sidebar,
nil,
container.NewBorder(
nil,
container.NewVBox(widget.NewSeparator(), infoBar),
nil,
nil,
contentArea,
),
)
// Layout Main Window
w.SetContent(mainLayout)
// Pre-select first tab
tabs.Select(ExplorerTabRank)
// Dependency check
checkDependencies(w)
// Set backend7z to store the backend 7-zip being used
backend7z := ""
// Using length instead of filepath.base, as it allows to differentiate between 7zzs and ./7zzs
// Restrict length to 5 letter in case of absolue path (./7zzs). Check the length first to prevent a crash
if len(root7zCmd) >= 5 {
backend7z = root7zCmd[len(root7zCmd)-5:]
} else {
// Fallback if the string is shorter than 5 letters
if root7zCmd == "7z" {
backend7z = "p7zip"
} else {
backend7z = root7zCmd
}
}
// Set initial value for the default record under Operations History
historyData = []operationLog{
{
ID: 0,
File: fmt.Sprintf("7-Zip GUI (%s) with '%s' as backend", version, backend7z),
OpType: "Initialized",
Status: "Ready",
Timestamp: time.Now().Format("15:04:05"),
},
}
// Check for updates asynchronously without blocking the UI
go checkForUpdates(w, a)
w.ShowAndRun()
}
func checkDependencies(w fyne.Window) {
// Check for 7zz in PATH
if _, err := exec.LookPath("7zz"); err == nil {
root7zCmd = "7zz"
return
}
// Check for 7zzs in PATH
if _, err := exec.LookPath("7zzs"); err == nil {
root7zCmd = "7zzs"
return
}
// Check for ./7zzs (placed in the same directory as the app)
local7zzsPath := getFullCmdPath("7zzs", w)
if info, err := os.Stat(local7zzsPath); err == nil && !info.IsDir() {
// Ensure the file has executable permissions (Unix/Linux)
if info.Mode().Perm()&0111 != 0 {
root7zCmd = local7zzsPath
return
}
}
// Check for 7z in PATH
if _, err := exec.LookPath("7z"); err == nil {
root7zCmd = "7z"
return
}
dialog.ShowInformation("7-Zip Not Found", "No 7z found to be installed or at recognized place in the system. We have automated workflow that ensures you have 7-Zip when you install this tool. It appears something may not worked correctly during install. It is recommended to either uninstall the tool, download latest copy and reinstall, so that you have a working copy of 7-Zip on your system, or install 7-Zip manually.", w)
}
// use this for 7zzs that sits beside our binary
func getFullCmdPath(appname string, w fyne.Window) string {
exePath, err := os.Executable()
if err != nil {
dialog.ShowError(fmt.Errorf("Failed to get executable path: %v", err), w)
}
realPath, err := filepath.EvalSymlinks(exePath)
if err != nil {
realPath = exePath
}
exeDir := filepath.Dir(realPath)
appnamePath := filepath.Join(exeDir, appname)
return appnamePath
}