feat: gtk

This commit is contained in:
2025-11-26 16:28:37 +03:00
parent 26556c53e8
commit 3e77a26b0a
12 changed files with 514 additions and 12 deletions

36
internal/ui/builder.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2025 Nikolai Papin
//
// This file is part of the Auto Attendance app that looks for
// self-attend QR-codes during lectures and opens their URLs in your
// browser.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package ui
import (
"github.com/gotk3/gotk3/gtk"
)
func NewBuilder() (*gtk.Builder, error) {
gtk.Init(nil)
builder, err := gtk.BuilderNew()
if err != nil {
return nil, err
}
return builder, nil
}

View File

@@ -1,7 +0,0 @@
package ui
import "github.com/gotk3/gotk3/gtk"
type Handlers struct {
gtk.AboutDialog
}

74
internal/ui/mainwindow.go Normal file
View File

@@ -0,0 +1,74 @@
// Copyright (c) 2025 Nikolai Papin
//
// This file is part of the Auto Attendance app that looks for
// self-attend QR-codes during lectures and opens their URLs in your
// browser.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package ui
import (
"git.weirdcat.su/weirdcat/auto-attendance/internal/ui/resources"
"github.com/gotk3/gotk3/gtk"
)
type MainWindow struct {
builder *gtk.Builder
window *gtk.Window
}
func (m *MainWindow) Start() {
m.window.ShowAll()
go func() { gtk.Main() }()
}
func NewMainWindow(builder *gtk.Builder) (*MainWindow, error) {
err := builder.AddFromString(string(resources.GladeMainWindow))
if err != nil {
return nil, err
}
windowObj, err := builder.GetObject("window_main")
if err != nil {
return nil, err
}
window := windowObj.(*gtk.Window)
window.Connect("destroy", func () {
gtk.MainQuit()
})
notebookObj, err := builder.GetObject("notebook_main")
if err != nil {
return nil, err
}
notebook := notebookObj.(*gtk.Notebook)
mainPageObj, err := builder.GetObject("page_main")
if err != nil {
return nil, err
}
mainPage := mainPageObj.(*gtk.Grid)
settingsPageObj, err := builder.GetObject("page_settings")
if err != nil {
return nil, err
}
settingsPage := settingsPageObj.(*gtk.Box)
notebook.SetTabLabelText(mainPage, "Overview")
notebook.SetTabLabelText(settingsPage, "Settings")
return &MainWindow{builder: builder, window: window}, nil
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -0,0 +1,30 @@
// Copyright (c) 2025 Nikolai Papin
//
// This file is part of the Auto Attendance app that looks for
// self-attend QR-codes during lectures and opens their URLs in your
// browser.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package resources
import _ "embed"
var (
//go:embed window.glade
GladeMainWindow []byte
//go:embed qrcode.png
QrCodePng []byte
)

View File

@@ -0,0 +1,367 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="lower">100</property>
<property name="upper">5000</property>
<property name="value">500</property>
<property name="step-increment">100</property>
<property name="page-increment">500</property>
</object>
<object class="GtkTextBuffer" id="console_output">
<property name="text" translatable="yes">[ERROR] 2025-11-26 14:24:15 - Failed to fetch data from API: Timeout occurred after 30 seconds.
[ERROR] 2025-11-26 14:24:30 - Exception: NullReferenceException in ModuleX: Object reference not set to an instance of an object.
</property>
</object>
<object class="GtkWindow" id="window_main">
<property name="name">window_main</property>
<property name="can-focus">False</property>
<property name="default-width">400</property>
<property name="default-height">600</property>
<property name="icon-name">zoom-best-fit</property>
<child>
<object class="GtkNotebook" id="notebook_main">
<property name="visible">True</property>
<property name="can-focus">True</property>
<child>
<!-- n-columns=1 n-rows=4 -->
<object class="GtkGrid" id="page_main">
<property name="name">App</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-homogeneous">True</property>
<property name="column-homogeneous">True</property>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="window-placement">bottom-left</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTextView" id="terminal-output">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="margin-start">10</property>
<property name="margin-end">10</property>
<property name="margin-top">10</property>
<property name="margin-bottom">10</property>
<property name="editable">False</property>
<property name="wrap-mode">word-char</property>
<property name="buffer">console_output</property>
<property name="input-purpose">terminal</property>
<property name="monospace">True</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<!-- n-columns=1 n-rows=1 -->
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-homogeneous">True</property>
<property name="column-homogeneous">True</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="baseline-position">top</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="height">3</property>
</packing>
</child>
</object>
<packing>
<property name="tab-expand">True</property>
<property name="tab-fill">False</property>
</packing>
</child>
<child>
<object class="GtkBox" id="page_settings">
<property name="name">Settings</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">20</property>
<property name="margin-end">20</property>
<property name="margin-top">20</property>
<property name="margin-bottom">20</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label-xalign">0</property>
<property name="shadow-type">none</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkCheckButton" id="auto-scan-check">
<property name="label" translatable="yes">Auto-scan for QR codes</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="beep-on-scan-check">
<property name="label" translatable="yes">Beep on successful scan</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Scanning Options</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label-xalign">0</property>
<property name="shadow-type">none</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">12</property>
<property name="margin-end">12</property>
<property name="margin-top">12</property>
<property name="margin-bottom">12</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Scan interval (ms):</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="scan-interval-spin">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="adjustment">adjustment1</property>
<property name="climb-rate">1</property>
<property name="snap-to-ticks">True</property>
<property name="numeric">True</property>
<property name="value">500</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Output format:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="output-format-combo">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="active">0</property>
<items>
<item translatable="yes">Text</item>
<item translatable="yes">JSON</item>
<item translatable="yes">XML</item>
<item translatable="yes">URL</item>
</items>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Advanced Settings</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save-settings-btn">
<property name="label" translatable="yes">Save Settings</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="halign">end</property>
<property name="margin-top">12</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
<property name="tab-expand">True</property>
</packing>
</child>
</object>
</child>
<child type="titlebar">
<object class="GtkHeaderBar" id="headerbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="title" translatable="yes">Qrminator</property>
<property name="subtitle" translatable="yes">Offline</property>
<property name="spacing">1</property>
<property name="show-close-button">True</property>
<child>
<object class="GtkSwitch" id="qr-handle-toggle">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="tooltip-text" translatable="yes">Toggle QR-searching</property>
<child internal-child="accessible">
<object class="AtkObject" id="qr-handle-toggle-atkobject">
<property name="AtkObject::accessible-name" translatable="yes">Toggle QR-handling</property>
<property name="AtkObject::accessible-description" translatable="yes">When enabled, program will search QR-codes on the screen and react to them according to user settings</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSpinner">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="active">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@@ -1 +0,0 @@
package ui