mirror of
https://github.com/jorenchik/mdemory.git
synced 2026-03-22 00:26:21 +00:00
add c++ project and translate from go partially
This commit is contained in:
1
src/go/compiler/.gitignore
vendored
Normal file
1
src/go/compiler/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
compiler
|
||||
19
src/go/compiler/api/api.go
Normal file
19
src/go/compiler/api/api.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/jorenchik/mdemory/src/compiler/lexer"
|
||||
"github.com/jorenchik/mdemory/src/compiler/parser"
|
||||
)
|
||||
|
||||
func Compile(fileContents string) ([]parser.Question, error) {
|
||||
tokens, err := lexer.TokenizeMdem([]rune(fileContents))
|
||||
if (err != nil) {
|
||||
return nil, err
|
||||
}
|
||||
questions, err := parser.ParseQuestions(tokens)
|
||||
if (err != nil) {
|
||||
return nil, err
|
||||
}
|
||||
return questions, err
|
||||
}
|
||||
|
||||
15
src/go/compiler/comperror/comperror.go
Normal file
15
src/go/compiler/comperror/comperror.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package comperror
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type PositionErr struct {
|
||||
Message string
|
||||
Row int32
|
||||
Column int32
|
||||
}
|
||||
|
||||
func (e PositionErr) Error() string {
|
||||
return fmt.Sprintf("%d:%d - %s", e.Row, e.Column, e.Message)
|
||||
}
|
||||
30
src/go/compiler/compiler.go
Normal file
30
src/go/compiler/compiler.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jorenchik/mdemory/src/compiler/api"
|
||||
)
|
||||
|
||||
func main() {
|
||||
file, err := os.ReadFile(
|
||||
"/home/jorenchik/Code/mdemory/src/compiler/input.mdem",
|
||||
)
|
||||
if (err != nil) {
|
||||
fmt.Printf(
|
||||
"Cannot open the input file: %s\n",
|
||||
err.Error(),
|
||||
)
|
||||
return
|
||||
}
|
||||
fileContents := string(file)
|
||||
|
||||
fmt.Println("Compilation started...")
|
||||
_, err = api.Compile(fileContents)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Println("Compilation completed")
|
||||
}
|
||||
15
src/go/compiler/deck.moml
Normal file
15
src/go/compiler/deck.moml
Normal file
@@ -0,0 +1,15 @@
|
||||
[MathTest]
|
||||
items = {graph_theory.networks, linear_algebra.*}
|
||||
base_interval = 2
|
||||
|
||||
[Projects]
|
||||
items = {}
|
||||
intervals = {2, 6, 12, 14}
|
||||
|
||||
[Programming]
|
||||
items = {programming.*}
|
||||
|
||||
[OldProgramming]
|
||||
items = {old_programming.*}
|
||||
disabled = false
|
||||
|
||||
3
src/go/compiler/go.mod
Normal file
3
src/go/compiler/go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module github.com/jorenchik/mdemory/src/compiler
|
||||
|
||||
go 1.22.5
|
||||
0
src/go/compiler/go.sum
Normal file
0
src/go/compiler/go.sum
Normal file
215
src/go/compiler/lexer/lexer.go
Normal file
215
src/go/compiler/lexer/lexer.go
Normal file
@@ -0,0 +1,215 @@
|
||||
package lexer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jorenchik/mdemory/src/compiler/comperror"
|
||||
)
|
||||
|
||||
var buffer []rune
|
||||
var row int32
|
||||
var column int32
|
||||
var previousRow int32
|
||||
var previousColumn int32
|
||||
var textStarted bool = false
|
||||
var identifierStarted bool = false
|
||||
|
||||
type TokenType int
|
||||
const (
|
||||
TextFragment TokenType = iota
|
||||
QuestionEnd
|
||||
ElementDashStart
|
||||
ElementPlusStart
|
||||
Identifier
|
||||
IdentifierStart
|
||||
IdentifierEnd
|
||||
SectionIdentifierStart
|
||||
SectionStart
|
||||
SectionEnd
|
||||
SOF
|
||||
EOF
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
TokenType TokenType;
|
||||
Content string;
|
||||
Row int32;
|
||||
Column int32;
|
||||
}
|
||||
|
||||
func (token Token) ToString() string {
|
||||
content := token.Content
|
||||
if (token.TokenType == TextFragment) {
|
||||
content = strings.Replace(
|
||||
strings.Trim(content, " "),
|
||||
"\n",
|
||||
"\\n",
|
||||
-1,
|
||||
)
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%20s: %35s %3d:%3d\n",
|
||||
ToString(&token.TokenType),
|
||||
content,
|
||||
token.Row,
|
||||
token.Column,
|
||||
)
|
||||
}
|
||||
|
||||
var tokens []Token
|
||||
|
||||
func makeTokenWithTokenBuffer(
|
||||
ttype TokenType,
|
||||
tokenLen int32,
|
||||
textType TokenType,
|
||||
) {
|
||||
if (len(strings.Trim(string(buffer), " \n\t")) - 1 > 0) {
|
||||
textFragment := []rune{}
|
||||
for i := 0; i < len(buffer) - int(tokenLen); i++ {
|
||||
element := buffer[i]
|
||||
textFragment = append(textFragment, element)
|
||||
}
|
||||
tokens = append(
|
||||
tokens,
|
||||
Token{
|
||||
TokenType: textType,
|
||||
Content: string(textFragment),
|
||||
Row: int32(previousRow),
|
||||
Column: int32(previousColumn),
|
||||
},
|
||||
)
|
||||
}
|
||||
tokens = append(
|
||||
tokens,
|
||||
Token{
|
||||
TokenType: ttype,
|
||||
Content: string(buffer[len(buffer)-int(tokenLen):]),
|
||||
Row: int32(row),
|
||||
Column: int32(column),
|
||||
},
|
||||
)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
buffer = []rune{}
|
||||
}
|
||||
|
||||
func TokenizeMdem(fileRunes []rune) ([]Token, error) {
|
||||
tokens = []Token{}
|
||||
buffer = []rune{}
|
||||
row = 1
|
||||
column = 1
|
||||
previousRow = -1
|
||||
previousColumn = -1
|
||||
textStarted = false
|
||||
|
||||
if (len(strings.Trim(string(fileRunes), " \n\t")) <= 0) {
|
||||
return []Token{}, nil
|
||||
}
|
||||
|
||||
for i := 0; i < len(fileRunes); i++ {
|
||||
c := fileRunes[i]
|
||||
|
||||
// AdvancePointer
|
||||
if (c == '\n') {
|
||||
row += 1
|
||||
column = 1
|
||||
}
|
||||
buffer = append(buffer, c)
|
||||
|
||||
// SkipWhitetext
|
||||
if !textStarted {
|
||||
if c == '\n' {
|
||||
previousRow += 1
|
||||
previousColumn = 1
|
||||
} else if (c == ' ') {
|
||||
previousColumn += 1
|
||||
} else {
|
||||
textStarted = true
|
||||
}
|
||||
}
|
||||
|
||||
// EmitTokens
|
||||
switch c {
|
||||
case '[':
|
||||
makeTokenWithTokenBuffer(IdentifierStart, 1, TextFragment)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
identifierStarted = true
|
||||
case ']':
|
||||
if !identifierStarted {
|
||||
return []Token{}, comperror.PositionErr{
|
||||
Message: "Cannot end identifier if it is not started",
|
||||
Row: row,
|
||||
Column: column,
|
||||
}
|
||||
|
||||
}
|
||||
makeTokenWithTokenBuffer(IdentifierEnd, 1, Identifier)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
identifierStarted = false
|
||||
case '#':
|
||||
makeTokenWithTokenBuffer(SectionIdentifierStart, 1, TextFragment)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
case '{':
|
||||
makeTokenWithTokenBuffer(SectionStart, 1, Identifier)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
case '}':
|
||||
makeTokenWithTokenBuffer(SectionEnd, 1, TextFragment)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
case '-':
|
||||
makeTokenWithTokenBuffer(ElementDashStart, 1, TextFragment)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
case '>':
|
||||
makeTokenWithTokenBuffer(QuestionEnd, 1, TextFragment)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
case '+':
|
||||
makeTokenWithTokenBuffer(ElementPlusStart, 1, TextFragment)
|
||||
previousRow = row
|
||||
previousColumn = column
|
||||
textStarted = false
|
||||
}
|
||||
column += 1
|
||||
}
|
||||
|
||||
// EmitEOF
|
||||
makeTokenWithTokenBuffer(EOF, 0, TextFragment)
|
||||
|
||||
if true {
|
||||
fmt.Printf("Lexer output:\n")
|
||||
for _, el := range tokens {
|
||||
fmt.Print(el.ToString())
|
||||
}
|
||||
fmt.Printf("End Lexer output\n")
|
||||
}
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func ToString (ttype *TokenType) string {
|
||||
switch *ttype {
|
||||
case TextFragment: return "text fragment"
|
||||
case QuestionEnd: return "question end symbol"
|
||||
case ElementDashStart: return "dash element start"
|
||||
case ElementPlusStart: return "plus element start"
|
||||
case Identifier: return "identifier"
|
||||
case IdentifierStart: return "start of identifier"
|
||||
case IdentifierEnd: return "end of identifier"
|
||||
case SectionIdentifierStart: return "identifier"
|
||||
case SectionStart: return "start of a section"
|
||||
case SectionEnd: return "end of a section"
|
||||
case EOF: return "end of a file"
|
||||
default: return "unrecognized token"
|
||||
}
|
||||
}
|
||||
302
src/go/compiler/parser/parser.go
Normal file
302
src/go/compiler/parser/parser.go
Normal file
@@ -0,0 +1,302 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jorenchik/mdemory/src/compiler/comperror"
|
||||
"github.com/jorenchik/mdemory/src/compiler/lexer"
|
||||
)
|
||||
|
||||
type Question interface {
|
||||
ToString() string
|
||||
}
|
||||
|
||||
type SingleAnswerQuestion struct {
|
||||
ID string
|
||||
Question string
|
||||
Answer string
|
||||
Section string
|
||||
}
|
||||
|
||||
type Choice struct {
|
||||
Answer string
|
||||
IsCorrect bool
|
||||
}
|
||||
|
||||
type MultipleChoiceQuestion struct {
|
||||
ID string
|
||||
Question string
|
||||
Choices []Choice
|
||||
Section string
|
||||
}
|
||||
|
||||
func (question SingleAnswerQuestion) ToString() string {
|
||||
return fmt.Sprintf(
|
||||
"%20s: section: %-10s id: %-10s %-30s: %-30s",
|
||||
"<Single choice>",
|
||||
question.Section,
|
||||
question.ID,
|
||||
strings.Trim(question.Question, "\t\n "),
|
||||
strings.Trim(question.Answer, "\t\n "),
|
||||
)
|
||||
}
|
||||
|
||||
func (question MultipleChoiceQuestion) ToString() string {
|
||||
acc := ""
|
||||
acc += fmt.Sprintf(
|
||||
"%20s: section: %-10s id: %-10s %-30s",
|
||||
"<Multi choice>",
|
||||
question.Section,
|
||||
question.ID,
|
||||
question.Question,
|
||||
)
|
||||
for _, el := range question.Choices {
|
||||
opener := '-'
|
||||
if el.IsCorrect {
|
||||
opener = '+'
|
||||
}
|
||||
acc += fmt.Sprintf("\t%c %s\n", opener, strings.Trim(el.Answer, "\t\n "))
|
||||
}
|
||||
return acc
|
||||
}
|
||||
|
||||
type QuestionElement struct {
|
||||
isDash bool
|
||||
content string
|
||||
}
|
||||
|
||||
var automata map[lexer.TokenType][]lexer.TokenType
|
||||
|
||||
func contains(s []lexer.TokenType, e lexer.TokenType) bool {
|
||||
for _, a := range s {
|
||||
if a == e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parserAutomata() map[lexer.TokenType][]lexer.TokenType {
|
||||
automata := make(map[lexer.TokenType][]lexer.TokenType)
|
||||
automata[lexer.TextFragment] = []lexer.TokenType{
|
||||
lexer.QuestionEnd,
|
||||
lexer.ElementDashStart,
|
||||
lexer.ElementPlusStart,
|
||||
lexer.SectionIdentifierStart,
|
||||
lexer.SectionStart,
|
||||
lexer.EOF,
|
||||
lexer.SectionEnd,
|
||||
}
|
||||
automata[lexer.QuestionEnd] = []lexer.TokenType{
|
||||
lexer.ElementDashStart,
|
||||
lexer.ElementPlusStart,
|
||||
}
|
||||
automata[lexer.ElementDashStart] = []lexer.TokenType{
|
||||
lexer.IdentifierStart,
|
||||
lexer.TextFragment,
|
||||
}
|
||||
automata[lexer.ElementPlusStart] = []lexer.TokenType{
|
||||
lexer.TextFragment,
|
||||
}
|
||||
automata[lexer.Identifier] = []lexer.TokenType{
|
||||
lexer.IdentifierEnd,
|
||||
lexer.SectionStart,
|
||||
}
|
||||
automata[lexer.IdentifierStart] = []lexer.TokenType{
|
||||
lexer.Identifier,
|
||||
}
|
||||
automata[lexer.IdentifierEnd] = []lexer.TokenType{
|
||||
lexer.TextFragment,
|
||||
}
|
||||
automata[lexer.SectionIdentifierStart] = []lexer.TokenType{
|
||||
lexer.Identifier,
|
||||
}
|
||||
automata[lexer.SectionStart] = []lexer.TokenType{
|
||||
lexer.ElementDashStart,
|
||||
lexer.SectionIdentifierStart,
|
||||
lexer.EOF,
|
||||
}
|
||||
automata[lexer.SectionEnd] = []lexer.TokenType{
|
||||
lexer.SectionIdentifierStart,
|
||||
lexer.ElementDashStart,
|
||||
lexer.EOF,
|
||||
}
|
||||
automata[lexer.SOF] = []lexer.TokenType{
|
||||
lexer.ElementDashStart,
|
||||
lexer.SectionIdentifierStart,
|
||||
lexer.EOF,
|
||||
}
|
||||
automata[lexer.EOF] = []lexer.TokenType{}
|
||||
return automata
|
||||
}
|
||||
|
||||
func capitalize(str string) string {
|
||||
if len(str) <= 0 {
|
||||
return str
|
||||
}
|
||||
firstCapital := strings.ToUpper(str[0:1])
|
||||
if len(str) == 1 {
|
||||
return firstCapital
|
||||
} else {
|
||||
return firstCapital + str[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func ValidateGrammar(tokens []lexer.Token) error {
|
||||
automata = parserAutomata()
|
||||
for i := 0; i < len(tokens)-1; i++ {
|
||||
token := tokens[i]
|
||||
nextToken := tokens[i+1]
|
||||
if !contains(automata[token.TokenType], nextToken.TokenType) {
|
||||
return comperror.PositionErr{
|
||||
Message: fmt.Sprintf(
|
||||
"%s cannot precede %s",
|
||||
capitalize(lexer.ToString(&token.TokenType)),
|
||||
lexer.ToString(&nextToken.TokenType),
|
||||
),
|
||||
Row: token.Row,
|
||||
Column: token.Column,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var DEBUG bool = true
|
||||
|
||||
func ParseQuestions(tokens []lexer.Token) ([]Question, error) {
|
||||
err := ValidateGrammar(tokens)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
questions := []Question{}
|
||||
section := ""
|
||||
i := 0
|
||||
if DEBUG {
|
||||
fmt.Printf("Parser output:\n")
|
||||
}
|
||||
for {
|
||||
if i >= len(tokens) {
|
||||
break
|
||||
}
|
||||
// - [identifier] question_token >
|
||||
if tokens[i].TokenType == lexer.ElementDashStart {
|
||||
var id string
|
||||
var question string
|
||||
var questionElements []QuestionElement
|
||||
|
||||
if tokens[i+1].TokenType == lexer.IdentifierStart {
|
||||
id = tokens[i+2].Content
|
||||
question = tokens[i+4].Content
|
||||
questionElements = []QuestionElement{}
|
||||
i += 6
|
||||
} else {
|
||||
id = ""
|
||||
question = tokens[i+1].Content
|
||||
questionElements = []QuestionElement{}
|
||||
i += 3
|
||||
}
|
||||
|
||||
for {
|
||||
// Pointer is on the start of an element
|
||||
// - a_question >
|
||||
// - [identifier] a_question >
|
||||
// - an_element
|
||||
// terminate if we encounter a question.
|
||||
if i+3 < len(tokens) &&
|
||||
tokens[i+3].TokenType != lexer.EOF {
|
||||
|
||||
offset := 0
|
||||
if tokens[i+1].TokenType == lexer.IdentifierStart {
|
||||
offset = 5
|
||||
} else {
|
||||
offset = 2
|
||||
}
|
||||
if i+offset < len(tokens) &&
|
||||
tokens[i+offset].TokenType == lexer.QuestionEnd {
|
||||
break
|
||||
}
|
||||
if offset == 5 && tokens[i+5].TokenType != lexer.QuestionEnd {
|
||||
return nil, comperror.PositionErr{
|
||||
Message: "Cannot have an identifier here",
|
||||
Row: tokens[i].Row,
|
||||
Column: tokens[i].Column,
|
||||
}
|
||||
}
|
||||
}
|
||||
if i+2 >= len(tokens) {
|
||||
break
|
||||
}
|
||||
questionElement := QuestionElement{}
|
||||
if tokens[i].TokenType == lexer.ElementDashStart {
|
||||
questionElement.isDash = true
|
||||
} else {
|
||||
questionElement.isDash = false
|
||||
}
|
||||
questionElement.content = tokens[i+1].Content
|
||||
questionElements = append(questionElements, questionElement)
|
||||
i += 2
|
||||
}
|
||||
if len(questionElements) > 1 {
|
||||
question := MultipleChoiceQuestion{
|
||||
ID: id,
|
||||
Question: question,
|
||||
}
|
||||
choices := []Choice{}
|
||||
for k := 0; k < len(questionElements); k++ {
|
||||
choice := Choice{}
|
||||
choice.Answer = questionElements[k].content
|
||||
choice.IsCorrect = !questionElements[k].isDash
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
if section != "" {
|
||||
question.Section = section
|
||||
}
|
||||
question.Choices = choices
|
||||
questions = append(questions, question)
|
||||
if DEBUG {
|
||||
fmt.Printf("%s", question.ToString())
|
||||
}
|
||||
} else if len(questionElements) == 1 {
|
||||
question := SingleAnswerQuestion{
|
||||
ID: id,
|
||||
Question: question,
|
||||
Answer: questionElements[0].content,
|
||||
}
|
||||
if section != "" {
|
||||
question.Section = section
|
||||
}
|
||||
questions = append(questions, question)
|
||||
if DEBUG {
|
||||
fmt.Printf("%s\n", question.ToString())
|
||||
}
|
||||
}
|
||||
} else if tokens[i].TokenType == lexer.SectionIdentifierStart {
|
||||
section = tokens[i+1].Content
|
||||
i += 3
|
||||
if DEBUG {
|
||||
fmt.Printf("Started section: %s\n", section)
|
||||
}
|
||||
} else if tokens[i].TokenType == lexer.SectionEnd {
|
||||
section = ""
|
||||
i += 1
|
||||
if DEBUG {
|
||||
fmt.Printf("Section ended: %s\n", section)
|
||||
}
|
||||
} else if tokens[i].TokenType == lexer.EOF {
|
||||
if DEBUG {
|
||||
fmt.Printf("File terminated: EOF\n")
|
||||
}
|
||||
break
|
||||
} else {
|
||||
return nil, comperror.PositionErr{
|
||||
Message: "Unexpeced token",
|
||||
Row: tokens[i].Row,
|
||||
Column: tokens[i].Column,
|
||||
}
|
||||
}
|
||||
}
|
||||
return questions, nil
|
||||
}
|
||||
2
src/go/mdemory-app-qt/.gitignore
vendored
Normal file
2
src/go/mdemory-app-qt/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
build/
|
||||
qtbox
|
||||
23
src/go/mdemory-app-qt/.vscode/launch.json
vendored
Normal file
23
src/go/mdemory-app-qt/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch Package",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${fileDirname}"
|
||||
},
|
||||
{
|
||||
"name": "Debug Go Program",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "exec",
|
||||
"program": "${workspaceFolder}/mdemory_debug",
|
||||
"args": []
|
||||
}
|
||||
]
|
||||
}
|
||||
12
src/go/mdemory-app-qt/go.mod
Normal file
12
src/go/mdemory-app-qt/go.mod
Normal file
@@ -0,0 +1,12 @@
|
||||
module github.com/jorenchik/mdemory/src/mdemory-app-qt
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/jorenchik/mdemory/src/compiler v0.0.0-00010101000000-000000000000
|
||||
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d
|
||||
)
|
||||
|
||||
require github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e // indirect
|
||||
|
||||
replace github.com/jorenchik/mdemory/src/compiler => ../compiler
|
||||
25
src/go/mdemory-app-qt/go.sum
Normal file
25
src/go/mdemory-app-qt/go.sum
Normal file
@@ -0,0 +1,25 @@
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e h1:XWcjeEtTFTOVA9Fs1w7n2XBftk5ib4oZrhzWk0B+3eA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d h1:T+d8FnaLSvM/1BdlDXhW4d5dr2F07bAbB+LpgzMxx+o=
|
||||
github.com/therecipe/qt v0.0.0-20200904063919-c0c124a5770d/go.mod h1:SUUR2j3aE1z6/g76SdD6NwACEpvCxb3fvG82eKbD6us=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190420063019-afa5a82059c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
BIN
src/go/mdemory-app-qt/main
Executable file
BIN
src/go/mdemory-app-qt/main
Executable file
Binary file not shown.
545
src/go/mdemory-app-qt/main.go
Normal file
545
src/go/mdemory-app-qt/main.go
Normal file
@@ -0,0 +1,545 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jorenchik/mdemory/src/compiler/api"
|
||||
"github.com/jorenchik/mdemory/src/compiler/comperror"
|
||||
"github.com/jorenchik/mdemory/src/compiler/parser"
|
||||
"github.com/therecipe/qt/core"
|
||||
"github.com/therecipe/qt/widgets"
|
||||
)
|
||||
|
||||
type Page struct {
|
||||
start int;
|
||||
end int;
|
||||
}
|
||||
|
||||
type PaginationButton struct {
|
||||
button *widgets.QToolButton;
|
||||
pageIdx int;
|
||||
}
|
||||
|
||||
var deckListLabel *widgets.QLabel
|
||||
var hMdemScroll *widgets.QVBoxLayout
|
||||
var mdemSpacer *widgets.QSpacerItem
|
||||
var spacerInitialized bool = false
|
||||
var workingPath string = "/home/jorenchik/Code/mdemory/memorybase"
|
||||
var questions [] parser.Question
|
||||
var paginationButtons []*widgets.QToolButton
|
||||
var paginationLabel *widgets.QLabel
|
||||
|
||||
var errorBox *widgets.QWidget
|
||||
var errorLabel *widgets.QLabel
|
||||
|
||||
|
||||
var multispacePattern *regexp.Regexp = regexp.MustCompile(`\s\s+`)
|
||||
|
||||
// var currentPage = 0
|
||||
var PER_PAGE = 10
|
||||
var pages []Page
|
||||
var currentPage = -1
|
||||
var prevButton *widgets.QToolButton
|
||||
var firstButton *widgets.QToolButton
|
||||
var lastButton *widgets.QToolButton
|
||||
var nextButton *widgets.QToolButton
|
||||
|
||||
type Mdem struct {
|
||||
wMdem *widgets.QWidget;
|
||||
wFrontText *widgets.QLabel;
|
||||
wBack *widgets.QWidget;
|
||||
backLabels []*widgets.QLabel;
|
||||
showButton *widgets.QToolButton;
|
||||
labelCount int;
|
||||
}
|
||||
|
||||
var mdems []*Mdem
|
||||
|
||||
func (mdem *Mdem) showBacklabels() {
|
||||
for i := range(len(mdem.backLabels)) {
|
||||
if i < mdem.labelCount {
|
||||
if !mdem.backLabels[i].IsVisible() {
|
||||
mdem.backLabels[i].Show()
|
||||
}
|
||||
} else {
|
||||
if mdem.backLabels[i].IsVisible() {
|
||||
mdem.backLabels[i].Hide()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func CreateMdem() *Mdem {
|
||||
|
||||
// DefineMdem
|
||||
wMdem := widgets.NewQWidget(nil, 0)
|
||||
vMdem := widgets.NewQVBoxLayout()
|
||||
|
||||
wMdem.SetLayout(vMdem)
|
||||
id := fmt.Sprintf("mdem_%d", 1)
|
||||
wMdem.SetObjectName(id)
|
||||
|
||||
// InitFront
|
||||
wFront := widgets.NewQWidget(nil, 0)
|
||||
hFront := widgets.NewQHBoxLayout()
|
||||
wFront.SetMinimumHeight(60)
|
||||
wFront.SetLayout(hFront)
|
||||
wFront.SetProperty("first", core.NewQVariant1(true))
|
||||
wMdem.SetStyleSheet(fmt.Sprintf(`
|
||||
QWidget#%s > QWidget {
|
||||
border-right: 1px solid gray;
|
||||
border-bottom: 1px solid gray;
|
||||
border-left: 1px solid gray;
|
||||
}
|
||||
QWidget#%s > QWidget[first="true"] {
|
||||
border-top: 1px solid gray;
|
||||
}
|
||||
`, id, id))
|
||||
|
||||
// AddFrontContent
|
||||
elFrontText := widgets.NewQLabel(nil, 0)
|
||||
hFront.AddWidget(elFrontText, 0, 0)
|
||||
hFront.AddStretch(1)
|
||||
showAnswer := widgets.NewQToolButton(nil)
|
||||
showAnswer.SetText("Show")
|
||||
hFront.AddWidget(showAnswer, 0, 0)
|
||||
|
||||
wBack := widgets.NewQWidget(nil, 0)
|
||||
hBack := widgets.NewQVBoxLayout()
|
||||
wBack.SetLayout(hBack)
|
||||
vMdem.AddWidget(wBack, 0, 0)
|
||||
|
||||
// AddBackContent
|
||||
var backLabels []*widgets.QLabel
|
||||
for range(20) {
|
||||
elBackText := widgets.NewQLabel(nil, 0)
|
||||
hBack.AddWidget(elBackText, 0, 0)
|
||||
backLabels = append(backLabels, elBackText)
|
||||
}
|
||||
|
||||
vMdem.AddWidget(wFront, 0, 0)
|
||||
vMdem.AddWidget(wBack, 0, 0)
|
||||
vMdem.SetContentsMargins(0, 0, 0, 0)
|
||||
vMdem.SetSpacing(0)
|
||||
|
||||
|
||||
wBack.Hide()
|
||||
wMdem.Hide()
|
||||
mdem := Mdem{
|
||||
wMdem,
|
||||
elFrontText,
|
||||
wBack,
|
||||
backLabels,
|
||||
showAnswer,
|
||||
0,
|
||||
}
|
||||
|
||||
mdem.showButton.ConnectClicked(func(checked bool) {
|
||||
if mdem.wBack.IsVisible() {
|
||||
mdem.wBack.Hide()
|
||||
mdem.showButton.SetText("Show")
|
||||
} else {
|
||||
mdem.wBack.Show()
|
||||
mdem.showButton.SetText("Hide")
|
||||
}
|
||||
mdem.showBacklabels()
|
||||
})
|
||||
|
||||
return &mdem
|
||||
}
|
||||
|
||||
func CreateMdems(questions *[]parser.Question) {
|
||||
hMdemScroll.RemoveItem(mdemSpacer)
|
||||
|
||||
for i := range mdems {
|
||||
if mdems[i].wMdem.IsVisible() {
|
||||
mdems[i].wMdem.Hide()
|
||||
}
|
||||
}
|
||||
|
||||
if len(*questions) > len(mdems) {
|
||||
for range(len(*questions) - len(mdems)) {
|
||||
mdem := CreateMdem()
|
||||
mdems = append(mdems, mdem)
|
||||
hMdemScroll.AddWidget(mdem.wMdem, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// destroy widgets
|
||||
transformAnswer := func(answer string) string {
|
||||
answer = strings.ReplaceAll(answer, "\t", " ")
|
||||
answer = strings.ReplaceAll(answer, "\n", " ")
|
||||
answer = multispacePattern.ReplaceAllString(answer, ` `)
|
||||
return answer
|
||||
}
|
||||
for i := range *questions {
|
||||
question := (*questions)[i]
|
||||
switch question.(type) {
|
||||
case parser.SingleAnswerQuestion:
|
||||
mdems[i].wFrontText.SetText(
|
||||
question.(parser.SingleAnswerQuestion).Question,
|
||||
)
|
||||
answer := question.(parser.SingleAnswerQuestion).Answer
|
||||
answer = transformAnswer(answer)
|
||||
answer = fmt.Sprintf("- %s", answer)
|
||||
mdems[i].backLabels[0].SetText(answer)
|
||||
if mdems[i].wBack.IsVisible() {
|
||||
mdems[i].wBack.Hide()
|
||||
}
|
||||
mdems[i].labelCount = 1
|
||||
case parser.MultipleChoiceQuestion:
|
||||
choices := question.(parser.MultipleChoiceQuestion).Choices
|
||||
mdems[i].wFrontText.SetText(
|
||||
question.(parser.MultipleChoiceQuestion).Question,
|
||||
)
|
||||
for k := range choices {
|
||||
answer := choices[k].Answer
|
||||
answer = transformAnswer(answer)
|
||||
answer = fmt.Sprintf("- %s", answer)
|
||||
if k < len(mdems[i].backLabels) {
|
||||
mdems[i].backLabels[k].SetText(answer)
|
||||
} else {
|
||||
label := widgets.NewQLabel2(answer, nil, 0)
|
||||
mdems[i].backLabels = append(
|
||||
mdems[i].backLabels,
|
||||
label,
|
||||
)
|
||||
mdems[i].wBack.Layout().AddWidget(label)
|
||||
}
|
||||
}
|
||||
mdems[i].labelCount = len(choices)
|
||||
}
|
||||
if !mdems[i].wMdem.IsVisible() {
|
||||
mdems[i].wMdem.Show()
|
||||
}
|
||||
}
|
||||
|
||||
hMdemScroll.AddItem(mdemSpacer)
|
||||
}
|
||||
|
||||
func MakePages() {
|
||||
pages = nil
|
||||
for i := 0; i < (len(questions) / PER_PAGE) + 1; i++ {
|
||||
startingIndex := PER_PAGE * i
|
||||
amount := PER_PAGE
|
||||
if (i == len(questions) / PER_PAGE) {
|
||||
amount = len(questions) % PER_PAGE
|
||||
}
|
||||
pages = append(
|
||||
pages,
|
||||
Page{startingIndex, startingIndex + amount},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var PAGINATION_COUNT int = 8
|
||||
var DISTANCE int = PAGINATION_COUNT / 2 - 1
|
||||
|
||||
func SwitchPage(pageIdx int) {
|
||||
currentPage = pageIdx
|
||||
for i := range(paginationButtons) {
|
||||
paginationButtons[i].Hide()
|
||||
}
|
||||
l := 0
|
||||
paginationLabel.SetText(fmt.Sprintf("Page: %d", pageIdx + 1))
|
||||
for i := range(mdems) {
|
||||
if mdems[i].wBack.IsVisible() {
|
||||
mdems[i].wBack.Hide()
|
||||
mdems[i].showButton.SetText("Show")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for k := -DISTANCE; k <= DISTANCE; k = k + 1 {
|
||||
if pageIdx + k >= 0 && pageIdx + k < len(pages) {
|
||||
button := paginationButtons[l]
|
||||
button.SetText(fmt.Sprintf("%d", pageIdx + k + 1))
|
||||
if pageIdx + k != pageIdx {
|
||||
button.Show()
|
||||
} else {
|
||||
button.Hide()
|
||||
}
|
||||
l++
|
||||
}
|
||||
}
|
||||
|
||||
if pageIdx > 0 && len(pages) > 1 {
|
||||
firstButton.Show()
|
||||
} else {
|
||||
firstButton.Hide()
|
||||
}
|
||||
if pageIdx < len(pages) - 1 && len(pages) > 1 {
|
||||
lastButton.Show()
|
||||
} else {
|
||||
lastButton.Hide()
|
||||
}
|
||||
if len(pages) > 0 &&
|
||||
currentPage < len(pages) - 1 {
|
||||
nextButton.Show()
|
||||
} else {
|
||||
nextButton.Hide()
|
||||
}
|
||||
if len(pages) > 0 &&
|
||||
currentPage >= 1 {
|
||||
prevButton.Show()
|
||||
} else {
|
||||
prevButton.Hide()
|
||||
}
|
||||
|
||||
page := pages[pageIdx]
|
||||
pageSlice := questions[page.start:page.end]
|
||||
CreateMdems(&pageSlice)
|
||||
}
|
||||
|
||||
func OpenModelFile(
|
||||
model *widgets.QFileSystemModel,
|
||||
index *core.QModelIndex,
|
||||
) {
|
||||
if model.IsDir(index) {
|
||||
return
|
||||
}
|
||||
if errorBox.IsVisible() {
|
||||
errorBox.Hide()
|
||||
}
|
||||
filePath := model.FilePath(index)
|
||||
fileContents, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
widgets.QMessageBox_Critical(
|
||||
nil,
|
||||
"Compilation error",
|
||||
err.Error(),
|
||||
widgets.QMessageBox__Ok,
|
||||
widgets.QMessageBox__Ok,
|
||||
)
|
||||
return
|
||||
}
|
||||
deckListLabel.SetText(fmt.Sprintf("Mdem: %s", filePath))
|
||||
start := time.Now().UnixMicro()
|
||||
questions, err = api.Compile(string(fileContents))
|
||||
if err != nil {
|
||||
var errText string
|
||||
switch err.(type) {
|
||||
case comperror.PositionErr:
|
||||
errText = fmt.Sprintf(
|
||||
"%s on line %d, column %d",
|
||||
err.(comperror.PositionErr).Message,
|
||||
err.(comperror.PositionErr).Row,
|
||||
err.(comperror.PositionErr).Column,
|
||||
)
|
||||
default:
|
||||
errText = err.Error()
|
||||
}
|
||||
errorLabel.SetText(errText)
|
||||
errorBox.Show()
|
||||
for i := range(mdems) {
|
||||
if mdems[i].wMdem.IsVisible() {
|
||||
mdems[i].wMdem.Hide()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
duration := float32(time.Now().UnixMicro()-start) / 1000
|
||||
fmt.Printf("Compilation took %.3fms", duration)
|
||||
|
||||
MakePages()
|
||||
SwitchPage(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// InitApp
|
||||
app := widgets.NewQApplication(len(os.Args), os.Args)
|
||||
window := widgets.NewQMainWindow(nil, 0)
|
||||
hSplitter := widgets.NewQSplitter(nil)
|
||||
window.SetWindowTitle("MDemory")
|
||||
window.SetMinimumSize2(400, 300)
|
||||
|
||||
leftWidget := widgets.NewQWidget(nil, 0)
|
||||
leftLayout := widgets.NewQVBoxLayout()
|
||||
mdemLabel := widgets.NewQLabel2("Mdems", nil, 0)
|
||||
model := widgets.NewQFileSystemModel(nil)
|
||||
mdemList := widgets.NewQTreeView(nil)
|
||||
leftWidget.SetLayout(leftLayout)
|
||||
leftLayout.AddWidget(mdemLabel, 0, 0)
|
||||
model.SetRootPath(workingPath)
|
||||
mdemList.SetModel(model)
|
||||
|
||||
rootIndex := model.Index2("/home/jorenchik/Code/mdemory/memorybase", 0)
|
||||
fmt.Printf("Root index: %s\n", model.FileName(rootIndex))
|
||||
mdemList.SetRootIndex(rootIndex)
|
||||
|
||||
mdemList.HideColumn(1)
|
||||
mdemList.HideColumn(2)
|
||||
mdemList.HideColumn(3)
|
||||
mdemList.ConnectDoubleClicked(
|
||||
func(index *core.QModelIndex) {
|
||||
OpenModelFile(model, index)
|
||||
},
|
||||
)
|
||||
|
||||
deckLabel := widgets.NewQLabel2("Decks", nil, 0)
|
||||
deckList := widgets.NewQListView(nil)
|
||||
leftLayout.AddWidget(mdemList, 1, 0)
|
||||
leftLayout.AddWidget(deckLabel, 0, 0)
|
||||
leftLayout.AddWidget(deckList, 1, 0)
|
||||
|
||||
rightWidget := widgets.NewQWidget(nil, 0)
|
||||
rightLayout := widgets.NewQVBoxLayout()
|
||||
rightWidget.SetLayout(rightLayout)
|
||||
|
||||
top := widgets.NewQWidget(nil, 0)
|
||||
hTop := widgets.NewQHBoxLayout()
|
||||
deckListLabel = widgets.NewQLabel(nil, 0)
|
||||
top.SetLayout(hTop)
|
||||
rightLayout.AddWidget(top, 0, 0)
|
||||
|
||||
hTop.AddWidget(deckListLabel, 0, 0)
|
||||
hTop.AddStretch(1)
|
||||
refresh := widgets.NewQToolButton(nil)
|
||||
practice := widgets.NewQToolButton(nil)
|
||||
shuffle := widgets.NewQToolButton(nil)
|
||||
hTop.AddWidget(refresh, 0, 0)
|
||||
hTop.AddWidget(shuffle, 0, 0)
|
||||
hTop.AddWidget(practice, 0, 0)
|
||||
refresh.SetText("Refresh")
|
||||
shuffle.SetText("Shuffle")
|
||||
shuffle.ConnectClicked(
|
||||
func(checked bool) {
|
||||
for i := range questions {
|
||||
j := rand.Intn(i + 1)
|
||||
questions[i], questions[j] = questions[j], questions[i]
|
||||
}
|
||||
SwitchPage(0)
|
||||
},
|
||||
)
|
||||
practice.SetText("Practice")
|
||||
|
||||
mdemScroll := widgets.NewQScrollArea(nil)
|
||||
mdemContainer := widgets.NewQWidget(nil, 0)
|
||||
hMdemScroll = widgets.NewQVBoxLayout()
|
||||
mdemScroll.SetWidget(mdemContainer)
|
||||
mdemScroll.SetWidgetResizable(true)
|
||||
mdemContainer.SetLayout(hMdemScroll)
|
||||
|
||||
errorBox = widgets.NewQWidget(nil, 0)
|
||||
hErrorBox := widgets.NewQHBoxLayout2(nil)
|
||||
errorBox.SetLayout(hErrorBox)
|
||||
errorLabel = widgets.NewQLabel(nil, 0)
|
||||
errorLabel.SetText("Error")
|
||||
errorBox.Layout().AddWidget(errorLabel)
|
||||
hErrorBox.AddStretch(1)
|
||||
hMdemScroll.AddWidget(errorBox, 0, 0)
|
||||
errorBox.SetObjectName("error")
|
||||
errorBox.SetStyleSheet(`
|
||||
QWidget#error {
|
||||
border: 1px solid #dc143c;
|
||||
background-color: white;
|
||||
padding: 10px 0px;
|
||||
}
|
||||
`)
|
||||
errorBox.Hide()
|
||||
|
||||
for i := 0; i < 40; i++ {
|
||||
mdem := CreateMdem()
|
||||
mdems = append(
|
||||
mdems,
|
||||
mdem,
|
||||
)
|
||||
hMdemScroll.AddWidget(mdem.wMdem, 0, 0)
|
||||
}
|
||||
mdemSpacer = widgets.NewQSpacerItem(
|
||||
40,
|
||||
40,
|
||||
widgets.QSizePolicy__Minimum,
|
||||
widgets.QSizePolicy__Expanding,
|
||||
)
|
||||
hMdemScroll.AddItem(mdemSpacer)
|
||||
|
||||
// CreateMdems
|
||||
rightLayout.AddWidget(mdemScroll, 1, 0)
|
||||
|
||||
// MakePagination
|
||||
pagination := widgets.NewQWidget(nil, 0)
|
||||
hPagination := widgets.NewQHBoxLayout()
|
||||
pagination.SetLayout(hPagination)
|
||||
|
||||
firstButton = widgets.NewQToolButton(nil)
|
||||
firstButton.SetText("<<")
|
||||
hPagination.AddWidget(firstButton, 0, 0)
|
||||
firstButton.Hide()
|
||||
firstButton.ConnectClicked(func(checked bool) {
|
||||
if len(pages) > 0 {
|
||||
SwitchPage(0)
|
||||
}
|
||||
})
|
||||
|
||||
prevButton = widgets.NewQToolButton(nil)
|
||||
prevButton.SetText("<")
|
||||
hPagination.AddWidget(prevButton, 0, 0)
|
||||
prevButton.Hide()
|
||||
prevButton.ConnectClicked(func(checked bool) {
|
||||
if len(pages) > 0 {
|
||||
SwitchPage(currentPage - 1)
|
||||
}
|
||||
})
|
||||
|
||||
for i := 0; i < PAGINATION_COUNT; i++ {
|
||||
elButton := widgets.NewQToolButton(nil)
|
||||
elButton.SetText(fmt.Sprintf("%d", i+1))
|
||||
hPagination.AddWidget(elButton, 0, 0)
|
||||
elButton.Hide()
|
||||
elButton.ConnectClicked(func(checked bool) {
|
||||
pageNum, err := strconv.ParseInt(elButton.Text(), 10, 64)
|
||||
_ = err
|
||||
pageIdx := int(pageNum) - 1
|
||||
if (pageIdx < len(pages)) {
|
||||
SwitchPage(pageIdx)
|
||||
}
|
||||
})
|
||||
paginationButtons = append(
|
||||
paginationButtons,
|
||||
elButton,
|
||||
)
|
||||
}
|
||||
|
||||
nextButton = widgets.NewQToolButton(nil)
|
||||
nextButton.SetText(">")
|
||||
hPagination.AddWidget(nextButton, 0, 0)
|
||||
nextButton.Hide()
|
||||
nextButton.ConnectClicked(func(checked bool) {
|
||||
if len(pages) > 0 {
|
||||
SwitchPage(currentPage + 1)
|
||||
}
|
||||
})
|
||||
|
||||
lastButton = widgets.NewQToolButton(nil)
|
||||
lastButton.SetText(">>")
|
||||
hPagination.AddWidget(lastButton, 0, 0)
|
||||
lastButton.Hide()
|
||||
lastButton.ConnectClicked(func(checked bool) {
|
||||
if len(pages) > 0 {
|
||||
SwitchPage(len(pages) - 1)
|
||||
}
|
||||
})
|
||||
|
||||
hPagination.AddStretch(1)
|
||||
paginationLabel = widgets.NewQLabel(nil, 0)
|
||||
hPagination.AddWidget(paginationLabel, 0, 0)
|
||||
|
||||
rightLayout.AddWidget(pagination, 0, 0)
|
||||
|
||||
hSplitter.AddWidget(leftWidget)
|
||||
hSplitter.AddWidget(rightWidget)
|
||||
hSplitter.SetStretchFactor(0, 1)
|
||||
hSplitter.SetStretchFactor(1, 3)
|
||||
window.SetCentralWidget(hSplitter)
|
||||
|
||||
window.Show()
|
||||
app.Exec()
|
||||
}
|
||||
Reference in New Issue
Block a user