forked from mbk-lab/rui_orig
Merge branch 'Work'
This commit is contained in:
commit
b0e51e0830
|
@ -1,3 +1,12 @@
|
|||
# v0.5.0
|
||||
|
||||
* NewApplication function and Start function of the Application interface were replaced by StartApp function
|
||||
* Added HasFocus function to the View interface
|
||||
* Added the UserAgent function to the Session interface
|
||||
* Added the following properties to TableView: "selection-mode", "allow-selection", "current", "current-style", "current-inactive-style"
|
||||
* Added the following events to TableView: "table-cell-selected", "table-cell-clicked", "table-row-selected", "table-row-clicked"
|
||||
* Bug fixing
|
||||
|
||||
# v0.4.0
|
||||
|
||||
* Added SetTitle and SetTitleColor function to the Session interface
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 Alexei Anoshenko
|
||||
Copyright (c) 2021-2022 Alexei Anoshenko
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
137
README-ru.md
137
README-ru.md
|
@ -21,28 +21,33 @@
|
|||
}
|
||||
|
||||
func main() {
|
||||
app := rui.NewApplication("Hello world", "icon.svg", createHelloWorldSession)
|
||||
app.Start("localhost:8000")
|
||||
rui.StartApp("localhost:8000", createHelloWorldSession, rui.AppParams{
|
||||
Title: "Hello world",
|
||||
Icon: "icon.svg",
|
||||
})
|
||||
}
|
||||
|
||||
В функции main создается rui приложение и запускается основной цикл.
|
||||
При создании приложения задаются 3 параметра: имя приложения, имя иконки и функция createHelloWorldSession.
|
||||
Функция createHelloWorldSession создает структуру реализующую интерфейс SessionContent:
|
||||
В функции main вызывается функция StartApp. Она создает rui приложение и запускает его основной цикл.
|
||||
Функция StartApp имеет 3 параметра:
|
||||
1) IP адрес по которому будет доступно приложение (в нашем примере это "localhost:8000")
|
||||
2) Фуекция создает структуру реализующую интерфейс SessionContent
|
||||
3) Дополнительные опциональные параметры (в нашем примере это заголовок и имя файла иконки)
|
||||
|
||||
Интерфейс SessionContent объявлен как:
|
||||
|
||||
type SessionContent interface {
|
||||
CreateRootView(session rui.Session) rui.View
|
||||
}
|
||||
|
||||
Для каждой новой сессии создается свой экземпляр структуры.
|
||||
|
||||
Функция CreateRootView интерфейса SessionContent создает корневой элемент.
|
||||
|
||||
Когда пользователь обращается к приложению набрав в браузере адрес "localhost:8000", то создается новая сессия,
|
||||
для нее создается новый экземпляр структуры helloWorldSession и в конце вызывается функция CreateRootView.
|
||||
Функция createRootView возвращает представление строки текста, создаваемое с помощью функции NewTextView.
|
||||
|
||||
Если вы хотите чтобы приложение было видно вне вашего компьютера, то поменяйте адрес в функции Start:
|
||||
|
||||
app.Start(rui.GetLocalIP() + ":80")
|
||||
rui.StartApp(rui.GetLocalIP() + ":80", ...
|
||||
|
||||
## Используемые типы данных
|
||||
|
||||
|
@ -3260,6 +3265,7 @@ Cell(row, column int) возвращает содержимое ячейки т
|
|||
* rui.Color
|
||||
* rui.View
|
||||
* fmt.Stringer
|
||||
* rui.VerticalTableJoin, rui.HorizontalTableJoin
|
||||
|
||||
Свойству "content" можно также присваивать следующие типы данных
|
||||
|
||||
|
@ -3314,11 +3320,11 @@ Cell(row, column int) возвращает содержимое ячейки т
|
|||
|
||||
В этом случае таблица будет иметь следующий вид
|
||||
|
||||
|------|----------------|
|
||||
| | |
|
||||
| |-------|--------|
|
||||
| | | |
|
||||
|------|-------|--------|
|
||||
|------|----------------|
|
||||
| | |
|
||||
| |-------|--------|
|
||||
| | | |
|
||||
|------|-------|--------|
|
||||
|
||||
Если в качестве значения свойства "content" используется [][]interface{}, то для объединения
|
||||
ячеек используются пустые структуры
|
||||
|
@ -3421,7 +3427,110 @@ TableColumnStyle объявлена как
|
|||
| 2 | CenterAlign | "center" | Выравнивание по центру |
|
||||
| 3, 4 | BaselineAlign | "baseline" | Выравнивание по базовой линии |
|
||||
|
||||
Для горизонтального выравнивания используется свойство "text-align"
|
||||
Для горизонтального выравнивания используется свойство "text-align".
|
||||
|
||||
Получить значение данного свойства можно с помощью функции
|
||||
|
||||
func GetTableVerticalAlign(view View, subviewID string) int
|
||||
|
||||
### Свойство "selection-mode"
|
||||
|
||||
Свойство "selection-mode" (константа SelectionMode) типа int определяет режим
|
||||
выделения (подсвечивания) элементов таблицы. Доступные режимы:
|
||||
|
||||
* NoneSelection (0). Режим по умолчанию. В данном режиме нельзя выделять элементы таблицы. Таблица не может
|
||||
получить фокус ввода.
|
||||
|
||||
* CellSelection (1). В данном режиме может выделяться (подсвечиваться) одна ячейка таблицы.
|
||||
Ячейка выделяется интерактивно с помощью мыши или клавиатуры (с использованием клавиш управления курсором).
|
||||
В данном режиме таблица может получить фокус ввода. В данном режиме таблица генерирует два вида
|
||||
событий: "table-cell-selected" и "table-cell-clicked" (о них ниже).
|
||||
|
||||
* RowSelection (2). В данном режиме может выделяться (подсвечиваться) только строка таблицы целиком.
|
||||
В данном режиме таблица похожа на ListView. Строка выделяется интерактивно с помощью мыши или клавиатуры
|
||||
(с использованием клавиш управления курсором). В данном режиме таблица может получить фокус ввода.
|
||||
В данном режиме таблица генерирует два вида событий: "table-row-selected" и "table-row-clicked" (о них ниже).
|
||||
|
||||
Получить значение данного свойства можно с помощью функции
|
||||
|
||||
func GetSelectionMode(view View, subviewID string) int
|
||||
|
||||
### Свойство "current"
|
||||
|
||||
Свойство "current" (константа Current) задает координаты выбранной ячейки/строки
|
||||
в виде структуры
|
||||
|
||||
type CellIndex struct {
|
||||
Row, Column int
|
||||
}
|
||||
|
||||
Если ячейка не выбрана, то значения полей Row и Column будут меньше 0.
|
||||
|
||||
В режиме RowSelection значение поля Column игнорируется. Также в данном режиме
|
||||
свойству "current" можно присваивать значение типа int (индекс строки).
|
||||
|
||||
Получить значение данного свойства можно с помощью функции
|
||||
|
||||
func GetTableCurrent(view View, subviewID string) CellIndex
|
||||
|
||||
### Свойство "allow-selection"
|
||||
|
||||
По умолчанию вы можете выделить любую ячейку/строку таблицы. Однако часто необходимо запретить
|
||||
выбор определенных элементов. Свойство "selection-mode" (константа SelectionMode) позволяет
|
||||
задать такое правило.
|
||||
|
||||
В режиме CellSelection данному свойству присваивается реализация интерфейса
|
||||
|
||||
type TableAllowCellSelection interface {
|
||||
AllowCellSelection(row, column int) bool
|
||||
}
|
||||
|
||||
а в режиме RowSelection - реализация интерфейса
|
||||
|
||||
type TableAllowRowSelection interface {
|
||||
AllowRowSelection(row int) bool
|
||||
}
|
||||
|
||||
Функция AllowCellSelection/AllowRowSelection должна возвращать "true" если ячейка/строка
|
||||
может быть выделена и "false" если ячейку/строку запрещено выделять.
|
||||
|
||||
### События "table-cell-selected" и "table-cell-clicked"
|
||||
|
||||
Событие "table-cell-selected" генерируется в режиме CellSelection когда пользователь выделил
|
||||
ячейку таблицы с помощью мыши или клавиатуры.
|
||||
|
||||
Событие "table-cell-clicked" возникает если пользователь кликает мышью по ячейке таблицы
|
||||
(при этом если она не выделена, то сначала возникает событие "table-cell-selected") или
|
||||
нажимает клавишу Enter или пробел
|
||||
|
||||
Основной слушатель данных событий имеет следующий формат:
|
||||
|
||||
func(TableView, int, int)
|
||||
|
||||
где второй аргумент это индекс строки ячейки, третий - индекс столбца
|
||||
|
||||
Можно также использовать слушателя следующего формата:
|
||||
|
||||
func(int, int)
|
||||
|
||||
### События "table-row-selected" и "table-row-clicked"
|
||||
|
||||
Событие "table-row-selected" генерируется в режиме RowSelection когда пользователь выделил
|
||||
строку таблицы с помощью мыши или клавиатуры.
|
||||
|
||||
Событие "table-row-clicked" возникает если пользователь кликает мышью по строке таблицы
|
||||
(при этом если она не выделена, то сначала возникает событие "table-row-selected") или
|
||||
нажимает клавишу Enter или пробел
|
||||
|
||||
Основной слушатель данных событий имеет следующий формат:
|
||||
|
||||
func(TableView, int)
|
||||
|
||||
где второй аргумент это индекс строки.
|
||||
|
||||
Можно также использовать слушателя следующего формата:
|
||||
|
||||
func(int)
|
||||
|
||||
## Пользовательский View
|
||||
|
||||
|
|
153
README.md
153
README.md
|
@ -1,6 +1,6 @@
|
|||
# RUI library
|
||||
|
||||
The RUI (Remoute User Interface) library is designed to create web applications in the go language.
|
||||
The RUI (Remote User Interface) library is designed to create web applications in the go language.
|
||||
|
||||
The peculiarity of the library is that all data processing is carried out on the server,
|
||||
and the browser is used as a thin client. WebSocket is used for client-server communication.
|
||||
|
@ -21,13 +21,19 @@ and the browser is used as a thin client. WebSocket is used for client-server co
|
|||
}
|
||||
|
||||
func main() {
|
||||
app := rui.NewApplication("Hello world", "icon.svg", createHelloWorldSession)
|
||||
app.Start("localhost:8000")
|
||||
rui.StartApp("localhost:8000", createHelloWorldSession, rui.AppParams{
|
||||
Title: "Hello world",
|
||||
Icon: "icon.svg",
|
||||
})
|
||||
}
|
||||
|
||||
In the main function, a rui application is created and the main loop is started.
|
||||
When creating an application, 3 parameters are set: the name of the application, the name of the icon, and the createHelloWorldSession function.
|
||||
The createHelloWorldSession function creates a structure that implements the SessionContent interface:
|
||||
In the main function, the StartApp function is called. It creates a rui app and runs its main loop.
|
||||
The StartApp function has 3 parameters:
|
||||
1) IP address where the application will be available (in our example it is "localhost:8000")
|
||||
2) The function creates a structure that implements the SessionContent interface
|
||||
3) Additional optional parameters (in our example, this is the title and the icon file name)
|
||||
|
||||
The SessionContent interface is declared as:
|
||||
|
||||
type SessionContent interface {
|
||||
CreateRootView(session rui.Session) rui.View
|
||||
|
@ -42,7 +48,7 @@ The createRootView function returns a representation of a text that is created u
|
|||
|
||||
If you want the application to be visible outside your computer, then change the address in the Start function:
|
||||
|
||||
app.Start(rui.GetLocalIP() + ":80")
|
||||
rui.StartApp(rui.GetLocalIP() + ":80", ...
|
||||
|
||||
## Used data types
|
||||
|
||||
|
@ -769,7 +775,7 @@ For this, the following properties of the SizeUnit type are used:
|
|||
If the x- and y-radii are the same, then you can use the auxiliary properties
|
||||
|
||||
| Property | Constant | Description |
|
||||
|----------------|--------------|----------------------------|
|
||||
|----------------|-------------|----------------------------|
|
||||
| "top-left" | TopLeft | top left corner radius |
|
||||
| "top-right" | TopRight | top right corner radius |
|
||||
| "bottom-left" | BottomLeft | bottom left corner radius |
|
||||
|
@ -1301,7 +1307,7 @@ Lines are split on newlines, on "br" elements, and optionally to fill inline box
|
|||
* Lines are wrapped on any spaces, including in the middle of a sequence of spaces.
|
||||
* Spaces take up space and do not hang at the ends of lines, which means they affect the internal dimensions (min-content and max-content).
|
||||
|
||||
The table below shows the behavior of various values of the "white-space" property.
|
||||
The table below shows the behavior of various values of the "white-space" property.
|
||||
|
||||
| | New lines | Spaces and Tabs | Text wrapping | End of line spaces | End-of-line other space separators |
|
||||
|-----------------------|-----------|-----------------|---------------|--------------------|------------------------------------|
|
||||
|
@ -1448,7 +1454,8 @@ You can get the value of this property using the function
|
|||
|
||||
#### "text-indent" property
|
||||
|
||||
The "text-indent" (TextIndent constant) SizeUnit property determines the size of the indent (empty space) before the first line of text.
|
||||
The "text-indent" (TextIndent constant) SizeUnit property determines the size of the indent (empty space)
|
||||
before the first line of text.
|
||||
|
||||
You can get the value of this property using the function
|
||||
|
||||
|
@ -1514,7 +1521,7 @@ You can get the value of this property using the function
|
|||
#### "writing-mode" property
|
||||
The "writing-mode" (WritingMode constant) int property defines how the lines of text are arranged
|
||||
vertically or horizontally, as well as the direction in which the lines are displayed.
|
||||
Possible values are:
|
||||
Possible values are:
|
||||
|
||||
| Value | Constant | Description |
|
||||
|:-----:|-----------------------|------------------------------------------------------------------|
|
||||
|
@ -1871,8 +1878,8 @@ The Touch structure describes a single touch and has the following fields
|
|||
| ClientY | float64 | The vertical position of the mouse relative to the upper left corner of the application |
|
||||
| ScreenX | float64 | Horizontal position of the mouse relative to the upper left corner of the screen |
|
||||
| ScreenY | float64 | Vertical position of the mouse relative to the upper left corner of the screen |
|
||||
| RadiusX | float64 | The x-radius of the ellipse, in pixels, that most closely delimits the area of contact with the screen. |
|
||||
| RadiusY | float64 | The y-radius of the ellipse, in pixels, that most closely delimits the area of contact with the screen. |
|
||||
| RadiusX | float64 | The x-radius of the ellipse, in pixels, that most closely delimits the area of contact with the screen. |
|
||||
| RadiusY | float64 | The y-radius of the ellipse, in pixels, that most closely delimits the area of contact with the screen. |
|
||||
| RotationAngle | float64 | The angle (in degrees) to rotate the ellipse clockwise, described by the radiusX and radiusY parameters, to best cover the contact area between the user and the surface. |
|
||||
| Force | float64 | The amount of pressure from 0.0 (no pressure) to 1.0 (maximum pressure) that the user applies to the surface. |
|
||||
|
||||
|
@ -2038,8 +2045,8 @@ relative to each other. The property can take the following values:
|
|||
| 3 | EndToStartOrientation | Child elements are laid out in a line from end to beginning. |
|
||||
|
||||
The start and end positions for StartToEndOrientation and EndToStartOrientation depend on the value
|
||||
of the "text-direction" property. For languages written from right to left (Arabic, Hebrew),
|
||||
the beginning is on the right, for other languages - on the left.
|
||||
of the "text-direction" property. For languages written from right to left (Arabic, Hebrew),
|
||||
the beginning is on the right, for other languages - on the left.
|
||||
|
||||
### "wrap" property
|
||||
|
||||
|
@ -2610,7 +2617,7 @@ For a multi-line editor, auto-wrap mode can be enabled. The bool property "wrap"
|
|||
If "wrap" is off (default), then horizontal scrolling is used.
|
||||
If enabled, the text wraps to a new line when the EditView border is reached.
|
||||
|
||||
The following functions can be used to get the values of the properties of an EditView:
|
||||
The following functions can be used to get the values of the properties of an EditView:
|
||||
|
||||
func GetText(view View, subviewID string) string
|
||||
func GetHint(view View, subviewID string) string
|
||||
|
@ -2665,7 +2672,7 @@ The value of the "date-picker-value" property can also be read using the functio
|
|||
|
||||
func GetNumberPickerValue(view View, subviewID string) float64
|
||||
|
||||
The entered values may be subject to restrictions. For this, the following properties are used:
|
||||
The entered values may be subject to restrictions. For this, the following properties are used:
|
||||
|
||||
| Property | Constant | Restriction |
|
||||
|--------------------|------------------|-------------------|
|
||||
|
@ -2678,7 +2685,7 @@ Assignments to these properties can be the same value types as "date-picker-valu
|
|||
By default, if "date-picker-type" is equal to NumberSlider, the minimum value is 0, maximum is 1.
|
||||
If "date-picker-type" is equal to NumberEditor, then the entered numbers, by default, are limited only by the range of float64 values.
|
||||
|
||||
You can read the values of these properties using the functions:
|
||||
You can read the values of these properties using the functions:
|
||||
|
||||
func GetNumberPickerMinMax(view View, subviewID string) (float64, float64)
|
||||
func GetNumberPickerStep(view View, subviewID string) float64
|
||||
|
@ -3027,8 +3034,8 @@ will be positioned relative to each other. The property can take the following v
|
|||
| 3 | EndToStartOrientation | Elements are arranged in a row from end to beginning. |
|
||||
|
||||
The start and end positions for StartToEndOrientation and EndToStartOrientation depend
|
||||
on the value of the "text-direction" property. For languages written from right to left
|
||||
(Arabic, Hebrew), the beginning is on the right, for other languages - on the left.
|
||||
on the value of the "text-direction" property. For languages written from right to left
|
||||
(Arabic, Hebrew), the beginning is on the right, for other languages - on the left.
|
||||
|
||||
You can get the value of this property using the function
|
||||
|
||||
|
@ -3224,6 +3231,7 @@ Cell(row, column int) returns the contents of a table cell. The Cell() function
|
|||
* rui.Color
|
||||
* rui.View
|
||||
* fmt.Stringer
|
||||
* rui.VerticalTableJoin, rui.HorizontalTableJoin
|
||||
|
||||
The "content" property can also be assigned the following data types
|
||||
|
||||
|
@ -3277,11 +3285,11 @@ The "row-span" property specifies how many cells to merge vertically, and the "c
|
|||
|
||||
In this case, the table will look like this
|
||||
|
||||
|------|----------------|
|
||||
| | |
|
||||
| |-------|--------|
|
||||
| | | |
|
||||
|------|-------|--------|
|
||||
|------+----------------|
|
||||
| | |
|
||||
| +-------+--------|
|
||||
| | | |
|
||||
|------+-------+--------|
|
||||
|
||||
If [][]interface{} is used as the value of the "content" property, then empty structures are used to merge cells
|
||||
|
||||
|
@ -3385,6 +3393,101 @@ the vertical alignment of data within a table cell. Valid values:
|
|||
|
||||
For horizontal alignment, use the "text-align" property
|
||||
|
||||
You can get the value of this property using the function
|
||||
|
||||
func GetTableVerticalAlign(view View, subviewID string) int
|
||||
|
||||
### "selection-mode" property
|
||||
|
||||
The "selection-mode" property (SelectionMode constant) of the int type determines the mode of selection (highlighting) of table elements. Available modes:
|
||||
|
||||
* NoneSelection (0). Default mode. In this mode, you cannot select table elements. The table cannot receive input focus.
|
||||
|
||||
* CellSelection (1). In this mode, one table cell can be selected (highlighted).
|
||||
The cell is selected interactively using the mouse or keyboard (using the cursor keys).
|
||||
In this mode, the table can receive input focus. In this mode, the table generates two types of events: "table-cell-selected" and "table-cell-clicked" (see below).
|
||||
|
||||
* RowSelection (2). In this mode, only the entire table row can be selected (highlighted).
|
||||
In this mode, the table is similar to a ListView. The row is selected interactively
|
||||
with the mouse or keyboard (using the cursor keys). In this mode, the table can receive input focus.
|
||||
In this mode, the table generates two types of events: "table-row-selected" and "table-row-clicked" (see below).
|
||||
|
||||
You can get the value of this property using the function
|
||||
|
||||
func GetSelectionMode(view View, subviewID string) int
|
||||
|
||||
### "current" property
|
||||
|
||||
The "current" property (Current constant) sets the coordinates of the selected cell/row as a structure
|
||||
|
||||
type CellIndex struct {
|
||||
Row, Column int
|
||||
}
|
||||
|
||||
If the cell is not selected, then the values of the Row and Column fields will be less than 0.
|
||||
|
||||
In RowSelection mode, the value of the Column field is ignored. Also in this mode,
|
||||
the "current" property can be assigned a value of type int (row index).
|
||||
|
||||
You can get the value of this property using the function
|
||||
|
||||
func GetTableCurrent(view View, subviewID string) CellIndex
|
||||
|
||||
### "allow-selection" property
|
||||
|
||||
By default, you can select any cell/row of the table. However, it is often necessary to disable the selection of certain elements. The "selection-mode" property (SelectionMode constant) allows you to set such a rule.
|
||||
|
||||
In CellSelection mode, this property is assigned the implementation of the interface
|
||||
|
||||
type TableAllowCellSelection interface {
|
||||
AllowCellSelection(row, column int) bool
|
||||
}
|
||||
|
||||
and in RowSelection mode this property is assigned the implementation of the interface
|
||||
|
||||
type TableAllowRowSelection interface {
|
||||
AllowRowSelection(row int) bool
|
||||
}
|
||||
|
||||
The AllowCellSelection/AllowRowSelection function must return "true"
|
||||
if the cell/row can be selected and "false" if the cell/row cannot be selected.
|
||||
|
||||
### "table-cell-selected" and "table-cell-clicked" events
|
||||
|
||||
The "table-cell-selected" event is fired in CellSelection mode when the user has selected
|
||||
a table cell with the mouse or keyboard.
|
||||
|
||||
The "table-cell-clicked" event occurs if the user clicks on a table cell (and if it is not selected,
|
||||
the "table-cell-selected" event occurs first) or presses the Enter or Space key.
|
||||
|
||||
The main listener for these events has the following format:
|
||||
|
||||
func(TableView, int, int)
|
||||
|
||||
where the second argument is the cell row index, the third argument is the column index
|
||||
|
||||
You can also use a listener in the following format:
|
||||
|
||||
func(int, int)
|
||||
|
||||
### "table-row-selected" and "table-row-clicked" events
|
||||
|
||||
The "table-row-selected" event is fired in RowSelection mode when the user has selected
|
||||
a table row with the mouse or keyboard.
|
||||
|
||||
The "table-row-clicked" event occurs if the user clicks on a table row (if it is not selected,
|
||||
the "table-row-selected" event fires first) or presses the Enter or Space key.
|
||||
|
||||
The main listener for these events has the following format:
|
||||
|
||||
func(TableView, int)
|
||||
|
||||
where the second argument is the row index.
|
||||
|
||||
You can also use a listener in the following format:
|
||||
|
||||
func(int)
|
||||
|
||||
## Custom View
|
||||
|
||||
A custom View must implement the CustomView interface, which extends the ViewsContainer and View interfaces.
|
||||
|
|
518
app_scripts.js
518
app_scripts.js
|
@ -42,9 +42,19 @@ function socketOpen() {
|
|||
}
|
||||
}
|
||||
|
||||
const lang = window.navigator.languages;
|
||||
const lang = window.navigator.language;
|
||||
if (lang) {
|
||||
message += ",languages=\"" + lang + "\"";
|
||||
message += ",language=\"" + lang + "\"";
|
||||
}
|
||||
|
||||
const langs = window.navigator.languages;
|
||||
if (langs) {
|
||||
message += ",languages=\"" + langs + "\"";
|
||||
}
|
||||
|
||||
const userAgent = window.navigator.userAgent
|
||||
if (userAgent) {
|
||||
message += ",user-agent=\"" + userAgent + "\"";
|
||||
}
|
||||
|
||||
const darkThemeMq = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
@ -615,11 +625,12 @@ function selectDropDownListItem(elementId, number) {
|
|||
|
||||
function listItemClickEvent(element, event) {
|
||||
event.stopPropagation();
|
||||
|
||||
var selected = false;
|
||||
if (element.classList) {
|
||||
selected = (element.classList.contains("ruiListItemFocused") || element.classList.contains("ruiListItemSelected"));
|
||||
} else {
|
||||
selected = element.className.indexOf("ruiListItemFocused") >= 0 || element.className.indexOf("ruiListItemSelected") >= 0;
|
||||
const focusStyle = getListFocusedItemStyle(element);
|
||||
const blurStyle = getListSelectedItemStyle(element);
|
||||
selected = (element.classList.contains(focusStyle) || element.classList.contains(blurStyle));
|
||||
}
|
||||
|
||||
var list = element.parentNode.parentNode
|
||||
|
@ -640,26 +651,33 @@ function getListItemNumber(itemId) {
|
|||
}
|
||||
}
|
||||
|
||||
function getStyleAttribute(element, attr, defValue) {
|
||||
var result = element.getAttribute(attr);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
return defValue;
|
||||
}
|
||||
|
||||
function getListFocusedItemStyle(element) {
|
||||
return getStyleAttribute(element, "data-focusitemstyle", "ruiListItemFocused");
|
||||
}
|
||||
|
||||
function getListSelectedItemStyle(element) {
|
||||
return getStyleAttribute(element, "data-bluritemstyle", "ruiListItemSelected");
|
||||
}
|
||||
|
||||
function selectListItem(element, item, needSendMessage) {
|
||||
var currentId = element.getAttribute("data-current");
|
||||
var message;
|
||||
var focusStyle = element.getAttribute("data-focusitemstyle");
|
||||
var blurStyle = element.getAttribute("data-bluritemstyle");
|
||||
|
||||
if (!focusStyle) {
|
||||
focusStyle = "ruiListItemFocused"
|
||||
}
|
||||
if (!blurStyle) {
|
||||
blurStyle = "ruiListItemSelected"
|
||||
}
|
||||
const focusStyle = getListFocusedItemStyle(element);
|
||||
const blurStyle = getListSelectedItemStyle(element);
|
||||
|
||||
if (currentId) {
|
||||
var current = document.getElementById(currentId);
|
||||
if (current) {
|
||||
if (current.classList) {
|
||||
current.classList.remove(focusStyle, blurStyle);
|
||||
} else { // IE < 10
|
||||
current.className = "ruiListItem";
|
||||
}
|
||||
if (sendMessage) {
|
||||
message = "itemUnselected{session=" + sessionID + ",id=" + element.id + "}";
|
||||
|
@ -671,14 +689,10 @@ function selectListItem(element, item, needSendMessage) {
|
|||
if (element === document.activeElement) {
|
||||
if (item.classList) {
|
||||
item.classList.add(focusStyle);
|
||||
} else { // IE < 10
|
||||
item.className = "ruiListItem " + focusStyle
|
||||
}
|
||||
} else {
|
||||
if (item.classList) {
|
||||
item.classList.add(blurStyle);
|
||||
} else { // IE < 10
|
||||
item.className = "ruiListItem " + blurStyle
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -690,6 +704,12 @@ function selectListItem(element, item, needSendMessage) {
|
|||
}
|
||||
}
|
||||
|
||||
if (item.scrollIntoViewIfNeeded) {
|
||||
item.scrollIntoViewIfNeeded()
|
||||
} else {
|
||||
item.scrollIntoView({block: "nearest", inline: "nearest"});
|
||||
}
|
||||
/*
|
||||
var left = item.offsetLeft - element.offsetLeft;
|
||||
if (left < element.scrollLeft) {
|
||||
element.scrollLeft = left;
|
||||
|
@ -708,7 +728,7 @@ function selectListItem(element, item, needSendMessage) {
|
|||
var bottom = top + item.offsetHeight
|
||||
if (bottom > element.scrollTop + element.clientHeight) {
|
||||
element.scrollTop = bottom - element.clientHeight;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
if (needSendMessage && message != undefined) {
|
||||
|
@ -801,24 +821,29 @@ function findBottomListItem(list, x, y) {
|
|||
return result
|
||||
}
|
||||
|
||||
function listViewKeyDownEvent(element, event) {
|
||||
var key;
|
||||
function getKey(event) {
|
||||
if (event.key) {
|
||||
key = event.key;
|
||||
} else if (event.keyCode) {
|
||||
return event.key;
|
||||
}
|
||||
|
||||
if (event.keyCode) {
|
||||
switch (event.keyCode) {
|
||||
case 13: key = "Enter"; break;
|
||||
case 32: key = " "; break;
|
||||
case 33: key = "PageUp"; break;
|
||||
case 34: key = "PageDown"; break;
|
||||
case 35: key = "End"; break;
|
||||
case 36: key = "Home"; break;
|
||||
case 37: key = "ArrowLeft"; break;
|
||||
case 38: key = "ArrowUp"; break;
|
||||
case 39: key = "ArrowRight"; break;
|
||||
case 40: key = "ArrowDown"; break;
|
||||
case 13: return "Enter";
|
||||
case 32: return " ";
|
||||
case 33: return "PageUp";
|
||||
case 34: return "PageDown";
|
||||
case 35: return "End";
|
||||
case 36: return "Home";
|
||||
case 37: return "ArrowLeft";
|
||||
case 38: return "ArrowUp";
|
||||
case 39: return "ArrowRight";
|
||||
case 40: return "ArrowDown";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function listViewKeyDownEvent(element, event) {
|
||||
const key = getKey(event);
|
||||
if (key) {
|
||||
var currentId = element.getAttribute("data-current");
|
||||
var current
|
||||
|
@ -885,20 +910,9 @@ function listViewFocusEvent(element, event) {
|
|||
if (currentId) {
|
||||
var current = document.getElementById(currentId);
|
||||
if (current) {
|
||||
var focusStyle = element.getAttribute("data-focusitemstyle");
|
||||
var blurStyle = element.getAttribute("data-bluritemstyle");
|
||||
if (!focusStyle) {
|
||||
focusStyle = "ruiListItemFocused"
|
||||
}
|
||||
if (!blurStyle) {
|
||||
blurStyle = "ruiListItemSelected"
|
||||
}
|
||||
|
||||
if (current.classList) {
|
||||
current.classList.remove(blurStyle);
|
||||
current.classList.add(focusStyle);
|
||||
} else { // IE < 10
|
||||
current.className = "ruiListItem " + focusStyle;
|
||||
current.classList.remove(getListSelectedItemStyle(element));
|
||||
current.classList.add(getListFocusedItemStyle(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -909,20 +923,9 @@ function listViewBlurEvent(element, event) {
|
|||
if (currentId) {
|
||||
var current = document.getElementById(currentId);
|
||||
if (current) {
|
||||
var focusStyle = element.getAttribute("data-focusitemstyle");
|
||||
var blurStyle = element.getAttribute("data-bluritemstyle");
|
||||
if (!focusStyle) {
|
||||
focusStyle = "ruiListItemFocused"
|
||||
}
|
||||
if (!blurStyle) {
|
||||
blurStyle = "ruiListItemSelected"
|
||||
}
|
||||
|
||||
if (current.classList) {
|
||||
current.classList.remove(focusStyle);
|
||||
current.classList.add(blurStyle);
|
||||
} else { // IE < 10
|
||||
current.className = "ruiListItem " + blurStyle;
|
||||
current.classList.remove(getListFocusedItemStyle(element));
|
||||
current.classList.add(getListSelectedItemStyle(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1373,3 +1376,396 @@ function setTitleColor(color) {
|
|||
function detailsEvent(element) {
|
||||
sendMessage("details-open{session=" + sessionID + ",id=" + element.id + ",open=" + (element.open ? "1}" : "0}"));
|
||||
}
|
||||
|
||||
function getTableFocusedItemStyle(element) {
|
||||
return getStyleAttribute(element, "data-focusitemstyle", "ruiCurrentTableCellFocused");
|
||||
}
|
||||
|
||||
function getTableSelectedItemStyle(element) {
|
||||
return getStyleAttribute(element, "data-bluritemstyle", "ruiCurrentTableCell");
|
||||
}
|
||||
|
||||
function tableViewFocusEvent(element, event) {
|
||||
var currentId = element.getAttribute("data-current");
|
||||
if (currentId) {
|
||||
var current = document.getElementById(currentId);
|
||||
if (current) {
|
||||
if (current.classList) {
|
||||
current.classList.remove(getTableSelectedItemStyle(element));
|
||||
current.classList.add(getTableFocusedItemStyle(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tableViewBlurEvent(element, event) {
|
||||
var currentId = element.getAttribute("data-current");
|
||||
if (currentId) {
|
||||
var current = document.getElementById(currentId);
|
||||
if (current && current.classList) {
|
||||
current.classList.remove(getTableFocusedItemStyle(element));
|
||||
current.classList.add(getTableSelectedItemStyle(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTableCellCursor(element, row, column) {
|
||||
const cellID = element.id + "-" + row + "-" + column;
|
||||
var cell = document.getElementById(cellID);
|
||||
if (!cell || cell.getAttribute("data-disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const focusStyle = getTableFocusedItemStyle(element);
|
||||
const oldCellID = element.getAttribute("data-current");
|
||||
if (oldCellID) {
|
||||
const oldCell = document.getElementById(oldCellID);
|
||||
if (oldCell && oldCell.classList) {
|
||||
oldCell.classList.remove(focusStyle);
|
||||
oldCell.classList.remove(getTableSelectedItemStyle(element));
|
||||
}
|
||||
}
|
||||
|
||||
cell.classList.add(focusStyle);
|
||||
element.setAttribute("data-current", cellID);
|
||||
if (cell.scrollIntoViewIfNeeded) {
|
||||
cell.scrollIntoViewIfNeeded()
|
||||
} else {
|
||||
cell.scrollIntoView({block: "nearest", inline: "nearest"});
|
||||
}
|
||||
|
||||
sendMessage("currentCell{session=" + sessionID + ",id=" + element.id +
|
||||
",row=" + row + ",column=" + column + "}");
|
||||
return true;
|
||||
}
|
||||
|
||||
function moveTableCellCursor(element, row, column, dr, dc) {
|
||||
const rows = element.getAttribute("data-rows");
|
||||
if (!rows) {
|
||||
return;
|
||||
}
|
||||
const columns = element.getAttribute("data-columns");
|
||||
if (!columns) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rowCount = parseInt(rows);
|
||||
const columnCount = parseInt(columns);
|
||||
|
||||
row += dr;
|
||||
column += dc;
|
||||
while (row >= 0 && row < rowCount && column >= 0 && column < columnCount) {
|
||||
if (setTableCellCursor(element, row, column)) {
|
||||
return;
|
||||
} else if (dr == 0) {
|
||||
var r2 = row - 1;
|
||||
while (r2 >= 0) {
|
||||
if (setTableCellCursor(element, r2, column)) {
|
||||
return;
|
||||
}
|
||||
r2--;
|
||||
}
|
||||
} else if (dc == 0) {
|
||||
var c2 = column - 1;
|
||||
while (c2 >= 0) {
|
||||
if (setTableCellCursor(element, row, c2)) {
|
||||
return;
|
||||
}
|
||||
c2--;
|
||||
}
|
||||
}
|
||||
row += dr;
|
||||
column += dc;
|
||||
}
|
||||
}
|
||||
|
||||
function tableViewCellKeyDownEvent(element, event) {
|
||||
const key = getKey(event);
|
||||
if (!key) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentId = element.getAttribute("data-current");
|
||||
if (!currentId || currentId == "") {
|
||||
switch (key) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
case "ArrowDown":
|
||||
case "ArrowUp":
|
||||
case "Home":
|
||||
case "End":
|
||||
case "PageUp":
|
||||
case "PageDown":
|
||||
const rows = element.getAttribute("data-rows");
|
||||
const columns = element.getAttribute("data-columns");
|
||||
if (rows && columns) {
|
||||
const rowCount = parseInt(rows);
|
||||
const columnCount = parseInt(rows);
|
||||
row = 0;
|
||||
while (row < rowCount) {
|
||||
column = 0;
|
||||
while (columns < columnCount) {
|
||||
if (setTableCellCursor(element, row, column)) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
column++;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const elements = currentId.split("-");
|
||||
if (elements.length >= 3) {
|
||||
const row = parseInt(elements[1], 10)
|
||||
const column = parseInt(elements[2], 10)
|
||||
|
||||
switch (key) {
|
||||
case " ":
|
||||
case "Enter":
|
||||
sendMessage("cellClick{session=" + sessionID + ",id=" + element.id +
|
||||
",row=" + row + ",column=" + column + "}");
|
||||
break;
|
||||
|
||||
case "ArrowLeft":
|
||||
moveTableCellCursor(element, row, column, 0, -1)
|
||||
break;
|
||||
|
||||
case "ArrowRight":
|
||||
moveTableCellCursor(element, row, column, 0, 1)
|
||||
break;
|
||||
|
||||
case "ArrowDown":
|
||||
moveTableCellCursor(element, row, column, 1, 0)
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
moveTableCellCursor(element, row, column, -1, 0)
|
||||
break;
|
||||
|
||||
case "Home":
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case "End":
|
||||
/*var newRow = rowCount-1;
|
||||
while (newRow > row) {
|
||||
if (setTableRowCursor(element, newRow)) {
|
||||
break;
|
||||
}
|
||||
newRow--;
|
||||
}*/
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case "PageUp":
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case "PageDown":
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
} else {
|
||||
element.setAttribute("data-current", "");
|
||||
}
|
||||
}
|
||||
|
||||
function setTableRowCursor(element, row) {
|
||||
const tableRowID = element.id + "-" + row;
|
||||
var tableRow = document.getElementById(tableRowID);
|
||||
if (!tableRow || tableRow.getAttribute("data-disabled")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const focusStyle = getTableFocusedItemStyle(element);
|
||||
const oldRowID = element.getAttribute("data-current");
|
||||
if (oldRowID) {
|
||||
const oldRow = document.getElementById(oldRowID);
|
||||
if (oldRow && oldRow.classList) {
|
||||
oldRow.classList.remove(focusStyle);
|
||||
oldRow.classList.remove(getTableSelectedItemStyle(element));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tableRow.classList.add(focusStyle);
|
||||
element.setAttribute("data-current", tableRowID);
|
||||
if (tableRow.scrollIntoViewIfNeeded) {
|
||||
tableRow.scrollIntoViewIfNeeded()
|
||||
} else {
|
||||
tableRow.scrollIntoView({block: "nearest", inline: "nearest"});
|
||||
}
|
||||
|
||||
sendMessage("currentRow{session=" + sessionID + ",id=" + element.id + ",row=" + row + "}");
|
||||
return true;
|
||||
}
|
||||
|
||||
function moveTableRowCursor(element, row, dr) {
|
||||
const rows = element.getAttribute("data-rows");
|
||||
if (!rows) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rowCount = parseInt(rows);
|
||||
row += dr;
|
||||
while (row >= 0 && row < rowCount) {
|
||||
if (setTableRowCursor(element, row)) {
|
||||
return;
|
||||
}
|
||||
row += dr;
|
||||
}
|
||||
}
|
||||
|
||||
function tableViewRowKeyDownEvent(element, event) {
|
||||
const key = getKey(event);
|
||||
if (key) {
|
||||
const currentId = element.getAttribute("data-current");
|
||||
if (currentId) {
|
||||
const elements = currentId.split("-");
|
||||
if (elements.length >= 2) {
|
||||
const row = parseInt(elements[1], 10);
|
||||
switch (key) {
|
||||
case " ":
|
||||
case "Enter":
|
||||
sendMessage("rowClick{session=" + sessionID + ",id=" + element.id + ",row=" + row + "}");
|
||||
break;
|
||||
|
||||
case "ArrowDown":
|
||||
moveTableRowCursor(element, row, 1)
|
||||
break;
|
||||
|
||||
case "ArrowUp":
|
||||
moveTableRowCursor(element, row, -1)
|
||||
break;
|
||||
|
||||
case "Home":
|
||||
var newRow = 0;
|
||||
while (newRow < row) {
|
||||
if (setTableRowCursor(element, newRow)) {
|
||||
break;
|
||||
}
|
||||
newRow++;
|
||||
}
|
||||
break;
|
||||
|
||||
case "End":
|
||||
var newRow = rowCount-1;
|
||||
while (newRow > row) {
|
||||
if (setTableRowCursor(element, newRow)) {
|
||||
break;
|
||||
}
|
||||
newRow--;
|
||||
}
|
||||
break;
|
||||
|
||||
case "PageUp":
|
||||
// TODO
|
||||
break;
|
||||
|
||||
case "PageDown":
|
||||
// TODO
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "ArrowLeft":
|
||||
case "ArrowRight":
|
||||
case "ArrowDown":
|
||||
case "ArrowUp":
|
||||
case "Home":
|
||||
case "End":
|
||||
case "PageUp":
|
||||
case "PageDown":
|
||||
const rows = element.getAttribute("data-rows");
|
||||
if (rows) {
|
||||
const rowCount = parseInt(rows);
|
||||
row = 0;
|
||||
while (row < rowCount) {
|
||||
if (setTableRowCursor(element, row)) {
|
||||
break;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
function tableCellClickEvent(element, event) {
|
||||
event.preventDefault();
|
||||
|
||||
const elements = element.id.split("-");
|
||||
if (elements.length < 3) {
|
||||
return
|
||||
}
|
||||
|
||||
const tableID = elements[0];
|
||||
const row = parseInt(elements[1], 10);
|
||||
const column = parseInt(elements[2], 10);
|
||||
const table = document.getElementById(tableID);
|
||||
if (table) {
|
||||
const selection = table.getAttribute("data-selection");
|
||||
if (selection == "cell") {
|
||||
const currentID = table.getAttribute("data-current");
|
||||
if (!currentID || currentID != element.ID) {
|
||||
setTableCellCursor(table, row, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage("cellClick{session=" + sessionID + ",id=" + tableID +
|
||||
",row=" + row + ",column=" + column + "}");
|
||||
}
|
||||
|
||||
function tableRowClickEvent(element, event) {
|
||||
event.preventDefault();
|
||||
|
||||
const elements = element.id.split("-");
|
||||
if (elements.length < 2) {
|
||||
return
|
||||
}
|
||||
|
||||
const tableID = elements[0];
|
||||
const row = parseInt(elements[1], 10);
|
||||
const table = document.getElementById(tableID);
|
||||
if (table) {
|
||||
const selection = table.getAttribute("data-selection");
|
||||
if (selection == "cell") {
|
||||
const currentID = table.getAttribute("data-current");
|
||||
if (!currentID || currentID != element.ID) {
|
||||
setTableRowCursor(table, row)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendMessage("rowClick{session=" + sessionID + ",id=" + tableID + ",row=" + row + "}");
|
||||
}
|
|
@ -27,19 +27,24 @@ var defaultThemeText string
|
|||
|
||||
// Application - app interface
|
||||
type Application interface {
|
||||
// Start - start the application life cycle
|
||||
Start(addr string)
|
||||
Finish()
|
||||
nextSessionID() int
|
||||
removeSession(id int)
|
||||
}
|
||||
|
||||
type application struct {
|
||||
name, icon string
|
||||
params AppParams
|
||||
createContentFunc func(Session) SessionContent
|
||||
sessions map[int]Session
|
||||
}
|
||||
|
||||
// AppParams defines parameters of the app
|
||||
type AppParams struct {
|
||||
Title string
|
||||
TitleColor Color
|
||||
Icon string
|
||||
}
|
||||
|
||||
func (app *application) getStartPage() string {
|
||||
buffer := allocStringBuilder()
|
||||
defer freeStringBuilder(buffer)
|
||||
|
@ -49,12 +54,19 @@ func (app *application) getStartPage() string {
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>`)
|
||||
buffer.WriteString(app.name)
|
||||
buffer.WriteString(app.params.Title)
|
||||
buffer.WriteString("</title>")
|
||||
if app.icon != "" {
|
||||
if app.params.Icon != "" {
|
||||
buffer.WriteString(`
|
||||
<link rel="icon" href="`)
|
||||
buffer.WriteString(app.icon)
|
||||
buffer.WriteString(app.params.Icon)
|
||||
buffer.WriteString(`">`)
|
||||
}
|
||||
|
||||
if app.params.TitleColor != 0 {
|
||||
buffer.WriteString(`
|
||||
<meta name="theme-color" content="`)
|
||||
buffer.WriteString(app.params.TitleColor.cssString())
|
||||
buffer.WriteString(`">`)
|
||||
}
|
||||
|
||||
|
@ -78,9 +90,8 @@ func (app *application) getStartPage() string {
|
|||
return buffer.String()
|
||||
}
|
||||
|
||||
func (app *application) init(name, icon string) {
|
||||
app.name = name
|
||||
app.icon = icon
|
||||
func (app *application) init(params AppParams) {
|
||||
app.params = params
|
||||
app.sessions = map[int]Session{}
|
||||
}
|
||||
|
||||
|
@ -277,12 +288,14 @@ func (app *application) startSession(params DataObject, events chan DataObject,
|
|||
return session, answerText
|
||||
}
|
||||
|
||||
// NewApplication - create the new application of the single view type.
|
||||
func NewApplication(name, icon string, createContentFunc func(Session) SessionContent) Application {
|
||||
// NewApplication - create the new application and start it
|
||||
func StartApp(addr string, createContentFunc func(Session) SessionContent, params AppParams) {
|
||||
app := new(application)
|
||||
app.init(name, icon)
|
||||
app.init(params)
|
||||
app.createContentFunc = createContentFunc
|
||||
return app
|
||||
|
||||
http.Handle("/", app)
|
||||
log.Fatal(http.ListenAndServe(addr, nil))
|
||||
}
|
||||
|
||||
func OpenBrowser(url string) bool {
|
||||
|
|
|
@ -148,11 +148,15 @@ func (customView *CustomViewData) Scroll() Frame {
|
|||
return customView.superView.Scroll()
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) HasFocus() bool {
|
||||
return customView.superView.HasFocus()
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) onResize(self View, x, y, width, height float64) {
|
||||
customView.superView.onResize(customView.superView, x, y, width, height)
|
||||
}
|
||||
|
||||
func (customView *CustomViewData) onItemResize(self View, index int, x, y, width, height float64) {
|
||||
func (customView *CustomViewData) onItemResize(self View, index string, x, y, width, height float64) {
|
||||
customView.superView.onItemResize(customView.superView, index, x, y, width, height)
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,10 @@ func (picker *datePickerData) Init(session Session) {
|
|||
picker.dateChangedListeners = []func(DatePicker, time.Time){}
|
||||
}
|
||||
|
||||
func (picker *datePickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *datePickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
switch tag {
|
||||
|
|
|
@ -213,24 +213,32 @@ theme {
|
|||
text-color = @ruiPopupTextColor,
|
||||
radius = 4px,
|
||||
shadow = _{spread-radius=4px, blur=16px, color=#80808080},
|
||||
}
|
||||
},
|
||||
ruiPopupTitle {
|
||||
background-color = @ruiPopupTitleColor,
|
||||
text-color = @ruiPopupTitleTextColor,
|
||||
min-height = 24px,
|
||||
}
|
||||
},
|
||||
ruiMessageText {
|
||||
padding-left = 64px,
|
||||
padding-right = 64px,
|
||||
padding-top = 32px,
|
||||
padding-bottom = 32px,
|
||||
}
|
||||
},
|
||||
ruiPopupMenuItem {
|
||||
padding-top = 4px,
|
||||
padding-bottom = 4px,
|
||||
padding-left = 8px,
|
||||
padding-right = 8px,
|
||||
}
|
||||
},
|
||||
ruiCurrentTableCell {
|
||||
background-color=@ruiSelectedColor,
|
||||
text-color=@ruiSelectedTextColor,
|
||||
},
|
||||
ruiCurrentTableCellFocused {
|
||||
background-color=@ruiHighlightColor,
|
||||
text-color=@ruiHighlightTextColor,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,13 @@ func textCanvasDemo(canvas rui.Canvas) {
|
|||
canvas.SetTextAlign(rui.LeftAlign)
|
||||
canvas.SetTextBaseline(rui.TopBaseline)
|
||||
|
||||
if canvas.View().Session().DarkTheme() {
|
||||
canvas.SetSolidColorFillStyle(0xFFFFFFFF)
|
||||
canvas.SetSolidColorStrokeStyle(0xFFFFFFFF)
|
||||
} else {
|
||||
canvas.SetSolidColorFillStyle(0xFF000000)
|
||||
canvas.SetSolidColorStrokeStyle(0xFF000000)
|
||||
}
|
||||
canvas.FillText(10, 10, "Default font")
|
||||
canvas.StrokeText(300, 10, "Default font")
|
||||
|
||||
|
@ -136,7 +142,11 @@ func textCanvasDemo(canvas rui.Canvas) {
|
|||
func textAlignCanvasDemo(canvas rui.Canvas) {
|
||||
canvas.Save()
|
||||
canvas.SetFont("sans-serif", rui.Pt(10))
|
||||
if canvas.View().Session().DarkTheme() {
|
||||
canvas.SetSolidColorFillStyle(0xFFFFFFFF)
|
||||
} else {
|
||||
canvas.SetSolidColorFillStyle(0xFF000000)
|
||||
}
|
||||
canvas.SetSolidColorStrokeStyle(0xFF00FFFF)
|
||||
|
||||
baseline := []string{"Alphabetic", "Top", "Middle", "Bottom", "Hanging", "Ideographic"}
|
||||
|
@ -176,18 +186,28 @@ func lineStyleCanvasDemo(canvas rui.Canvas) {
|
|||
canvas.SetLineWidth(1)
|
||||
y := float64(40 + 20*i)
|
||||
canvas.DrawLine(10, y, 180, y)
|
||||
if canvas.View().Session().DarkTheme() {
|
||||
canvas.SetSolidColorStrokeStyle(0xFFFFFFFF)
|
||||
} else {
|
||||
canvas.SetSolidColorStrokeStyle(0xFF000000)
|
||||
}
|
||||
|
||||
canvas.SetLineWidth(10)
|
||||
canvas.SetLineCap(i)
|
||||
canvas.DrawLine(20, y, 170, y)
|
||||
canvas.FillText(200, y, cap)
|
||||
}
|
||||
|
||||
canvas.SetSolidColorStrokeStyle(0xFF0000FF)
|
||||
if canvas.View().Session().DarkTheme() {
|
||||
canvas.SetSolidColorStrokeStyle(0xFFFFFFFF)
|
||||
canvas.SetSolidColorFillStyle(0xFF00FFFF)
|
||||
} else {
|
||||
canvas.SetSolidColorStrokeStyle(0xFF000000)
|
||||
canvas.SetSolidColorFillStyle(0xFF0000FF)
|
||||
}
|
||||
canvas.SetFont("courier", rui.Pt(12))
|
||||
canvas.FillText(80, 115, "SetLineJoin(...)")
|
||||
|
||||
canvas.SetSolidColorStrokeStyle(0xFF000000)
|
||||
canvas.SetLineWidth(10)
|
||||
canvas.SetLineCap(rui.ButtCap)
|
||||
|
||||
|
@ -206,14 +226,11 @@ func lineStyleCanvasDemo(canvas rui.Canvas) {
|
|||
canvas.StrokePath(path)
|
||||
canvas.FillText(210, y+20, join)
|
||||
}
|
||||
|
||||
canvas.SetSolidColorStrokeStyle(0xFF0000FF)
|
||||
canvas.SetFont("courier", rui.Pt(12))
|
||||
canvas.FillText(20, 300, "SetLineDash([]float64{16, 8, 4, 8}, ...)")
|
||||
|
||||
canvas.SetFont("courier", rui.Pt(10))
|
||||
canvas.SetLineDash([]float64{16, 8, 4, 8}, 0)
|
||||
canvas.SetSolidColorStrokeStyle(0xFF000000)
|
||||
canvas.SetLineWidth(4)
|
||||
|
||||
canvas.SetLineCap(rui.ButtCap)
|
||||
|
@ -246,8 +263,13 @@ func transformCanvasDemo(canvas rui.Canvas) {
|
|||
y1 := y0 + float64((ny-1)*20)
|
||||
|
||||
canvas.SetFont("serif", rui.Pt(10))
|
||||
canvas.SetSolidColorFillStyle(rui.Black)
|
||||
|
||||
if canvas.View().Session().DarkTheme() {
|
||||
canvas.SetSolidColorStrokeStyle(0xFFFFFFFF)
|
||||
canvas.SetSolidColorFillStyle(0xFFFFFFFF)
|
||||
} else {
|
||||
canvas.SetSolidColorStrokeStyle(0xFF000000)
|
||||
canvas.SetSolidColorFillStyle(0xFF000000)
|
||||
}
|
||||
canvas.SetTextAlign(rui.CenterAlign)
|
||||
canvas.SetTextBaseline(rui.BottomBaseline)
|
||||
for i := 0; i < nx; i++ {
|
||||
|
@ -266,7 +288,11 @@ func transformCanvasDemo(canvas rui.Canvas) {
|
|||
}
|
||||
|
||||
canvas.SetFont("courier", rui.Pt(14))
|
||||
canvas.SetSolidColorFillStyle(rui.Black)
|
||||
if canvas.View().Session().DarkTheme() {
|
||||
canvas.SetSolidColorFillStyle(0xFFFFFFFF)
|
||||
} else {
|
||||
canvas.SetSolidColorFillStyle(0xFF000000)
|
||||
}
|
||||
canvas.SetTextAlign(rui.CenterAlign)
|
||||
canvas.SetTextBaseline(rui.TopBaseline)
|
||||
|
||||
|
|
11
demo/main.go
11
demo/main.go
|
@ -113,10 +113,6 @@ func (demo *demoSession) CreateRootView(session rui.Session) rui.View {
|
|||
|
||||
rui.Set(demo.rootView, "rootTitleButton", rui.ClickEvent, demo.clickMenuButton)
|
||||
demo.showPage(0)
|
||||
if color, ok := rui.StringToColor("#ffc0ded9"); ok {
|
||||
session.SetTitleColor(color)
|
||||
}
|
||||
|
||||
return demo.rootView
|
||||
}
|
||||
|
||||
|
@ -158,11 +154,14 @@ func (demo *demoSession) showPage(index int) {
|
|||
func main() {
|
||||
rui.ProtocolInDebugLog = true
|
||||
rui.AddEmbedResources(&resources)
|
||||
app := rui.NewApplication("RUI demo", "icon.svg", createDemo)
|
||||
|
||||
//addr := rui.GetLocalIP() + ":8080"
|
||||
addr := "localhost:8000"
|
||||
fmt.Print(addr)
|
||||
rui.OpenBrowser("http://" + addr)
|
||||
app.Start(addr)
|
||||
rui.StartApp(addr, createDemo, rui.AppParams{
|
||||
Title: "RUI demo",
|
||||
Icon: "icon.svg",
|
||||
TitleColor: rui.Color(0xffc0ded9),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ GridLayout {
|
|||
content = [
|
||||
GridLayout {
|
||||
id = mouseEventsTest, cell-horizontal-align = center, cell-vertical-align = center,
|
||||
height = 150%,
|
||||
height = 100%,
|
||||
border = _{ style = solid, width = 1px, color = gray},
|
||||
content = [
|
||||
TextView {
|
||||
|
|
|
@ -32,6 +32,12 @@ GridLayout {
|
|||
DropDownList { row = 5, column = 1, id = tableFootStyle, current = 0, items = ["none", "tableFoot1", "rui.Params"]},
|
||||
Checkbox { row = 6, column = 0:1, id = tableRowStyle, content = "Row style" },
|
||||
Checkbox { row = 7, column = 0:1, id = tableColumnStyle, content = "Column style" },
|
||||
TextView { row = 8, text = "Selection mode" },
|
||||
DropDownList { row = 8, column = 1, id = tableSelectionMode, current = 0, items = ["none", "cell", "row"]},
|
||||
Checkbox { row = 9, column = 0:1, id = tableDisableHead, content = "Disable head selection" },
|
||||
Checkbox { row = 10, column = 0:1, id = tableDisableFoot, content = "Disable foot selection" },
|
||||
TextView { row = 11, text = "Vertical align in cells" },
|
||||
DropDownList { row = 11, column = 1, id = tableVerticalAlign, current = 0, items = ["top", "bottom", "center", "baseline"]},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@ -40,6 +46,31 @@ GridLayout {
|
|||
}
|
||||
`
|
||||
|
||||
type demoTableAllowSelection struct {
|
||||
index []int
|
||||
}
|
||||
|
||||
func (allow *demoTableAllowSelection) AllowCellSelection(row, column int) bool {
|
||||
return allow.AllowRowSelection(row)
|
||||
}
|
||||
|
||||
func (allow *demoTableAllowSelection) AllowRowSelection(row int) bool {
|
||||
if allow.index != nil {
|
||||
for _, index := range allow.index {
|
||||
if index == row {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func newDemoTableAllowSelection(index []int) *demoTableAllowSelection {
|
||||
result := new(demoTableAllowSelection)
|
||||
result.index = index
|
||||
return result
|
||||
}
|
||||
|
||||
func createTableViewDemo(session rui.Session) rui.View {
|
||||
view := rui.CreateViewFromText(session, tableViewDemoText)
|
||||
if view == nil {
|
||||
|
@ -93,6 +124,49 @@ func createTableViewDemo(session rui.Session) rui.View {
|
|||
}
|
||||
}
|
||||
|
||||
rui.Set(view, "tableSelectionMode", rui.DropDownEvent, func(list rui.DropDownList, number int) {
|
||||
rui.Set(view, "demoTableView1", rui.SelectionMode, number)
|
||||
switch rui.GetCurrent(view, "tableSelectionMode") {
|
||||
case rui.CellSelection:
|
||||
// TODO
|
||||
|
||||
case rui.RowSelection:
|
||||
// TODO
|
||||
}
|
||||
})
|
||||
|
||||
rui.Set(view, "tableDisableHead", rui.CheckboxChangedEvent, func(checked bool) {
|
||||
if checked {
|
||||
if rui.IsCheckboxChecked(view, "tableDisableFoot") {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, newDemoTableAllowSelection([]int{0, 1, 11}))
|
||||
} else {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, newDemoTableAllowSelection([]int{0, 1}))
|
||||
}
|
||||
} else {
|
||||
if rui.IsCheckboxChecked(view, "tableDisableFoot") {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, newDemoTableAllowSelection([]int{11}))
|
||||
} else {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
rui.Set(view, "tableDisableFoot", rui.CheckboxChangedEvent, func(checked bool) {
|
||||
if checked {
|
||||
if rui.IsCheckboxChecked(view, "tableDisableHead") {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, newDemoTableAllowSelection([]int{0, 1, 11}))
|
||||
} else {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, newDemoTableAllowSelection([]int{11}))
|
||||
}
|
||||
} else {
|
||||
if rui.IsCheckboxChecked(view, "tableDisableHead") {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, newDemoTableAllowSelection([]int{0, 1}))
|
||||
} else {
|
||||
rui.Set(view, "demoTableView1", rui.AllowSelection, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
rui.Set(view, "tableCellGap", rui.DropDownEvent, func(list rui.DropDownList, number int) {
|
||||
if number == 0 {
|
||||
rui.Set(view, "demoTableView1", rui.Gap, rui.Px(0))
|
||||
|
@ -202,5 +276,9 @@ func createTableViewDemo(session rui.Session) rui.View {
|
|||
}
|
||||
})
|
||||
|
||||
rui.Set(view, "tableVerticalAlign", rui.DropDownEvent, func(list rui.DropDownList, number int) {
|
||||
rui.Set(view, "demoTableView1", rui.TableVerticalAlign, number)
|
||||
})
|
||||
|
||||
return view
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ GridLayout {
|
|||
content = [
|
||||
GridLayout {
|
||||
id = touchEventsTest, cell-horizontal-align = center, cell-vertical-align = center,
|
||||
height = 150%,
|
||||
height = 100%,
|
||||
border = _{ style = solid, width = 1px, color = gray},
|
||||
content = [
|
||||
TextView {
|
||||
|
|
|
@ -39,6 +39,10 @@ func (list *dropDownListData) Init(session Session) {
|
|||
list.dropDownListener = []func(DropDownList, int){}
|
||||
}
|
||||
|
||||
func (list *dropDownListData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (list *dropDownListData) Remove(tag string) {
|
||||
list.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
|
|
@ -63,6 +63,10 @@ func (edit *editViewData) Init(session Session) {
|
|||
edit.tag = "EditView"
|
||||
}
|
||||
|
||||
func (edit *editViewData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (edit *editViewData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
switch tag {
|
||||
|
|
|
@ -89,6 +89,10 @@ func (picker *filePickerData) Init(session Session) {
|
|||
picker.fileSelectedListeners = []func(FilePicker, []FileInfo){}
|
||||
}
|
||||
|
||||
func (picker *filePickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *filePickerData) Files() []FileInfo {
|
||||
return picker.files
|
||||
}
|
||||
|
|
|
@ -140,13 +140,11 @@ func getFocusListeners(view View, subviewID string, tag string) []func(View) {
|
|||
}
|
||||
|
||||
func focusEventsHtml(view View, buffer *strings.Builder) {
|
||||
for tag, js := range focusEvents {
|
||||
if value := view.getRaw(tag); value != nil {
|
||||
if listeners, ok := value.([]func(View)); ok && len(listeners) > 0 {
|
||||
if view.Focusable() {
|
||||
for _, js := range focusEvents {
|
||||
buffer.WriteString(js.jsEvent + `="` + js.jsFunc + `(this, event)" `)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetFocusListeners returns a FocusListener list. If there are no listeners then the empty list is returned
|
||||
|
|
17
listView.go
17
listView.go
|
@ -929,6 +929,8 @@ func (listView *listViewData) htmlProperties(self View, buffer *strings.Builder)
|
|||
buffer.WriteString(strconv.Itoa(current))
|
||||
buffer.WriteRune('"')
|
||||
}
|
||||
|
||||
listView.viewData.htmlProperties(self, buffer)
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1084,15 +1086,13 @@ func (listView *listViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
func (listView *listViewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
switch command {
|
||||
case "itemSelected":
|
||||
if text, ok := data.PropertyValue(`number`); ok {
|
||||
if number, err := strconv.Atoi(text); err == nil {
|
||||
if number, ok := dataIntProperty(data, `number`); ok {
|
||||
listView.properties[Current] = number
|
||||
for _, listener := range listView.selectedListeners {
|
||||
listener(listView, number)
|
||||
}
|
||||
listView.propertyChangedEvent(Current)
|
||||
}
|
||||
}
|
||||
|
||||
case "itemUnselected":
|
||||
if _, ok := listView.properties[Current]; ok {
|
||||
|
@ -1163,9 +1163,14 @@ func (listView *listViewData) onItemClick() {
|
|||
}
|
||||
}
|
||||
|
||||
func (listView *listViewData) onItemResize(self View, index int, x, y, width, height float64) {
|
||||
if index >= 0 && index < len(listView.itemFrame) {
|
||||
listView.itemFrame[index] = Frame{Left: x, Top: y, Width: width, Height: height}
|
||||
func (listView *listViewData) onItemResize(self View, index string, x, y, width, height float64) {
|
||||
n, err := strconv.Atoi(index)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
} else if n >= 0 && n < len(listView.itemFrame) {
|
||||
listView.itemFrame[n] = Frame{Left: x, Top: y, Width: width, Height: height}
|
||||
} else {
|
||||
ErrorLogF(`Invalid ListView item index: %d`, n)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -168,6 +168,10 @@ func (player *mediaPlayerData) Init(session Session) {
|
|||
player.tag = "MediaPlayer"
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (player *mediaPlayerData) Remove(tag string) {
|
||||
player.remove(strings.ToLower(tag))
|
||||
}
|
||||
|
|
|
@ -50,6 +50,10 @@ func (picker *numberPickerData) Init(session Session) {
|
|||
picker.numberChangedListeners = []func(NumberPicker, float64){}
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *numberPickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
switch tag {
|
||||
|
|
|
@ -412,6 +412,11 @@ var enumProperties = map[string]struct {
|
|||
"",
|
||||
[]string{"none", "metadata", "auto"},
|
||||
},
|
||||
SelectionMode: {
|
||||
[]string{"none", "cell", "row"},
|
||||
"",
|
||||
[]string{"none", "cell", "row"},
|
||||
},
|
||||
}
|
||||
|
||||
func notCompatibleType(tag string, value interface{}) {
|
||||
|
|
|
@ -18,7 +18,7 @@ func (view *viewData) onResize(self View, x, y, width, height float64) {
|
|||
}
|
||||
}
|
||||
|
||||
func (view *viewData) onItemResize(self View, index int, x, y, width, height float64) {
|
||||
func (view *viewData) onItemResize(self View, index string, x, y, width, height float64) {
|
||||
}
|
||||
|
||||
func (view *viewData) setFrameListener(tag string, value interface{}) bool {
|
||||
|
|
23
session.go
23
session.go
|
@ -33,6 +33,10 @@ type Session interface {
|
|||
Color(tag string) (Color, bool)
|
||||
// SetCustomTheme set the custom theme
|
||||
SetCustomTheme(name string) bool
|
||||
// UserAgent returns the "user-agent" text of the client browser
|
||||
UserAgent() string
|
||||
// RemoteAddr returns the client address.
|
||||
RemoteAddr() string
|
||||
// Language returns the current session language
|
||||
Language() string
|
||||
// SetLanguage set the current session language
|
||||
|
@ -106,6 +110,7 @@ type sessionData struct {
|
|||
touchScreen bool
|
||||
textDirection int
|
||||
pixelRatio float64
|
||||
userAgent string
|
||||
language string
|
||||
languages []string
|
||||
checkboxOff string
|
||||
|
@ -148,12 +153,20 @@ func newSession(app Application, id int, customTheme string, params DataObject)
|
|||
session.touchScreen = (value == "1" || value == "true")
|
||||
}
|
||||
|
||||
if value, ok := params.PropertyValue("user-agent"); ok {
|
||||
session.userAgent = value
|
||||
}
|
||||
|
||||
if value, ok := params.PropertyValue("direction"); ok {
|
||||
if value == "rtl" {
|
||||
session.textDirection = RightToLeftDirection
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := params.PropertyValue("language"); ok {
|
||||
session.language = value
|
||||
}
|
||||
|
||||
if value, ok := params.PropertyValue("languages"); ok {
|
||||
session.languages = strings.Split(value, ",")
|
||||
}
|
||||
|
@ -378,15 +391,11 @@ func (session *sessionData) handleResize(data DataObject) {
|
|||
}
|
||||
if viewID, ok := obj.PropertyValue("id"); ok {
|
||||
if n := strings.IndexRune(viewID, '-'); n > 0 {
|
||||
if index, err := strconv.Atoi(viewID[n+1:]); err == nil {
|
||||
if view := session.viewByHTMLID(viewID[:n]); view != nil {
|
||||
view.onItemResize(view, index, getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height"))
|
||||
view.onItemResize(view, viewID[n+1:], getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height"))
|
||||
} else {
|
||||
ErrorLogF(`View with id == %s not found`, viewID[:n])
|
||||
}
|
||||
} else {
|
||||
ErrorLogF(`Invalid view id == %s not found`, viewID)
|
||||
}
|
||||
} else if view := session.viewByHTMLID(viewID); view != nil {
|
||||
view.onResize(view, getFloat("x"), getFloat("y"), getFloat("width"), getFloat("height"))
|
||||
view.setScroll(getFloat("scroll-x"), getFloat("scroll-y"), getFloat("scroll-width"), getFloat("scroll-height"))
|
||||
|
@ -423,3 +432,7 @@ func (session *sessionData) SetTitle(title string) {
|
|||
func (session *sessionData) SetTitleColor(color Color) {
|
||||
session.runScript(`setTitleColor("` + color.cssString() + `");`)
|
||||
}
|
||||
|
||||
func (session *sessionData) RemoteAddr() string {
|
||||
return session.brige.remoteAddr()
|
||||
}
|
||||
|
|
|
@ -328,6 +328,10 @@ func (session *sessionData) radiobuttonOnImage() string {
|
|||
return session.radiobuttonOn
|
||||
}
|
||||
|
||||
func (session *sessionData) UserAgent() string {
|
||||
return session.userAgent
|
||||
}
|
||||
|
||||
func (session *sessionData) Language() string {
|
||||
if session.language != "" {
|
||||
return session.language
|
||||
|
|
|
@ -1,23 +1,66 @@
|
|||
package rui
|
||||
|
||||
// TableAdapter describes the TableView content
|
||||
type TableAdapter interface {
|
||||
// RowCount returns number of rows in the table
|
||||
RowCount() int
|
||||
|
||||
// ColumnCount returns number of columns in the table
|
||||
ColumnCount() int
|
||||
|
||||
// Cell returns the contents of a table cell. The function can return elements of the following types:
|
||||
// * string
|
||||
// * rune
|
||||
// * float32, float64
|
||||
// * integer values: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64
|
||||
// * bool
|
||||
// * rui.Color
|
||||
// * rui.View
|
||||
// * fmt.Stringer
|
||||
// * rui.VerticalTableJoin, rui.HorizontalTableJoin
|
||||
Cell(row, column int) interface{}
|
||||
}
|
||||
|
||||
// TableColumnStyle describes the style of TableView columns.
|
||||
// To set column styles, you must either implement the TableColumnStyle interface in the table adapter
|
||||
// or assign its separate implementation to the "column-style" property.
|
||||
type TableColumnStyle interface {
|
||||
ColumnStyle(column int) Params
|
||||
}
|
||||
|
||||
// TableRowStyle describes the style of TableView rows.
|
||||
// To set row styles, you must either implement the TableRowStyle interface in the table adapter
|
||||
// or assign its separate implementation to the "row-style" property.
|
||||
type TableRowStyle interface {
|
||||
RowStyle(row int) Params
|
||||
}
|
||||
|
||||
// TableCellStyle describes the style of TableView cells.
|
||||
// To set row cells, you must either implement the TableCellStyle interface in the table adapter
|
||||
// or assign its separate implementation to the "cell-style" property.
|
||||
type TableCellStyle interface {
|
||||
CellStyle(row, column int) Params
|
||||
}
|
||||
|
||||
// TableAllowCellSelection determines whether TableView cell selection is allowed.
|
||||
// It is only used if the "selection-mode" property is set to CellSelection (1).
|
||||
// To set cell selection allowing, you must either implement the TableAllowCellSelection interface
|
||||
// in the table adapter or assign its separate implementation to the "allow-selection" property.
|
||||
type TableAllowCellSelection interface {
|
||||
AllowCellSelection(row, column int) bool
|
||||
}
|
||||
|
||||
// TableAllowRowSelection determines whether TableView row selection is allowed.
|
||||
// It is only used if the "selection-mode" property is set to RowSelection (2).
|
||||
// To set row selection allowing, you must either implement the TableAllowRowSelection interface
|
||||
// in the table adapter or assign its separate implementation to the "allow-selection" property.
|
||||
type TableAllowRowSelection interface {
|
||||
AllowRowSelection(row int) bool
|
||||
}
|
||||
|
||||
// SimpleTableAdapter is implementation of TableAdapter where the content
|
||||
// defines as [][]interface{}.
|
||||
// When you assign [][]interface{} value to the "content" property, it is converted to SimpleTableAdapter
|
||||
type SimpleTableAdapter interface {
|
||||
TableAdapter
|
||||
TableCellStyle
|
||||
|
@ -28,6 +71,9 @@ type simpleTableAdapter struct {
|
|||
columnCount int
|
||||
}
|
||||
|
||||
// TextTableAdapter is implementation of TableAdapter where the content
|
||||
// defines as [][]string.
|
||||
// When you assign [][]string value to the "content" property, it is converted to TextTableAdapter
|
||||
type TextTableAdapter interface {
|
||||
TableAdapter
|
||||
}
|
||||
|
@ -37,12 +83,17 @@ type textTableAdapter struct {
|
|||
columnCount int
|
||||
}
|
||||
|
||||
// NewTextTableAdapter is an auxiliary structure. It used as cell content and
|
||||
// specifies that the cell should be merged with the one above it
|
||||
type VerticalTableJoin struct {
|
||||
}
|
||||
|
||||
// HorizontalTableJoin is an auxiliary structure. It used as cell content and
|
||||
// specifies that the cell should be merged with the one before it
|
||||
type HorizontalTableJoin struct {
|
||||
}
|
||||
|
||||
// NewSimpleTableAdapter creates the new SimpleTableAdapter
|
||||
func NewSimpleTableAdapter(content [][]interface{}) SimpleTableAdapter {
|
||||
if content == nil {
|
||||
return nil
|
||||
|
@ -137,6 +188,7 @@ func (adapter *simpleTableAdapter) CellStyle(row, column int) Params {
|
|||
return params
|
||||
}
|
||||
|
||||
// NewTextTableAdapter creates the new TextTableAdapter
|
||||
func NewTextTableAdapter(content [][]string) TextTableAdapter {
|
||||
if content == nil {
|
||||
return nil
|
||||
|
@ -329,3 +381,14 @@ func (table *tableViewData) getColumnStyle() TableColumnStyle {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) getCellStyle() TableCellStyle {
|
||||
for _, tag := range []string{CellStyle, Content} {
|
||||
if value := table.getRaw(tag); value != nil {
|
||||
if style, ok := value.(TableCellStyle); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
689
tableView.go
689
tableView.go
|
@ -9,143 +9,233 @@ import (
|
|||
const (
|
||||
// TableVerticalAlign is the constant for the "table-vertical-align" property tag.
|
||||
// The "table-vertical-align" int property sets the vertical alignment of the content inside a table cell.
|
||||
// Valid values are LeftAlign (0), RightAlign (1), CenterAlign (2), and BaselineAlign (3, 4)
|
||||
// Valid values are TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3, 4)
|
||||
TableVerticalAlign = "table-vertical-align"
|
||||
|
||||
// HeadHeight is the constant for the "head-height" property tag.
|
||||
// The "head-height" int property sets the number of rows in the table header.
|
||||
// The default value is 0 (no header)
|
||||
HeadHeight = "head-height"
|
||||
|
||||
// HeadStyle is the constant for the "head-style" property tag.
|
||||
// The "head-style" string property sets the header style name
|
||||
HeadStyle = "head-style"
|
||||
|
||||
// FootHeight is the constant for the "foot-height" property tag.
|
||||
// The "foot-height" int property sets the number of rows in the table footer.
|
||||
// The default value is 0 (no footer)
|
||||
FootHeight = "foot-height"
|
||||
|
||||
// FootStyle is the constant for the "foot-style" property tag.
|
||||
// The "foot-style" string property sets the footer style name
|
||||
FootStyle = "foot-style"
|
||||
|
||||
// RowSpan is the constant for the "row-span" property tag.
|
||||
// The "row-span" int property sets the number of table row to span.
|
||||
// Used only when specifying cell parameters in the implementation of TableCellStyle
|
||||
RowSpan = "row-span"
|
||||
|
||||
// ColumnSpan is the constant for the "column-span" property tag.
|
||||
// The "column-span" int property sets the number of table column to span.
|
||||
// Used only when specifying cell parameters in the implementation of TableCellStyle
|
||||
ColumnSpan = "column-span"
|
||||
|
||||
// RowStyle is the constant for the "row-style" property tag.
|
||||
// The "row-style" property sets the adapter which specifies styles of each table row.
|
||||
// This property can be assigned or by an implementation of TableRowStyle interface, or by an array of Params.
|
||||
RowStyle = "row-style"
|
||||
|
||||
// ColumnStyle is the constant for the "column-style" property tag.
|
||||
// The "column-style" property sets the adapter which specifies styles of each table column.
|
||||
// This property can be assigned or by an implementation of TableColumnStyle interface, or by an array of Params.
|
||||
ColumnStyle = "column-style"
|
||||
|
||||
// CellStyle is the constant for the "cell-style" property tag.
|
||||
// The "cell-style" property sets the adapter which specifies styles of each table cell.
|
||||
// This property can be assigned only by an implementation of TableCellStyle interface.
|
||||
CellStyle = "cell-style"
|
||||
|
||||
// CellPadding is the constant for the "cell-padding" property tag.
|
||||
// The "cell-padding" Bounds property sets the padding area on all four sides of a table call at once.
|
||||
// An element's padding area is the space between its content and its border.
|
||||
CellPadding = "cell-padding"
|
||||
|
||||
// CellPaddingLeft is the constant for the "cell-padding-left" property tag.
|
||||
// The "cell-padding-left" SizeUnit property sets the width of the padding area to the left of a cell content.
|
||||
// An element's padding area is the space between its content and its border.
|
||||
CellPaddingLeft = "cell-padding-left"
|
||||
|
||||
// CellPaddingRight is the constant for the "cell-padding-right" property tag.
|
||||
// The "cell-padding-right" SizeUnit property sets the width of the padding area to the left of a cell content.
|
||||
// An element's padding area is the space between its content and its border.
|
||||
CellPaddingRight = "cell-padding-right"
|
||||
|
||||
// CellPaddingTop is the constant for the "cell-padding-top" property tag.
|
||||
// The "cell-padding-top" SizeUnit property sets the height of the padding area to the top of a cell content.
|
||||
// An element's padding area is the space between its content and its border.
|
||||
CellPaddingTop = "cell-padding-top"
|
||||
|
||||
// CellPaddingBottom is the constant for the "cell-padding-bottom" property tag.
|
||||
// The "cell-padding-bottom" SizeUnit property sets the height of the padding area to the bottom of a cell content.
|
||||
CellPaddingBottom = "cell-padding-bottom"
|
||||
|
||||
// CellBorder is the constant for the "cell-border" property tag.
|
||||
// The "cell-border" property sets a table cell's border. It sets the values of a border width, style, and color.
|
||||
// This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation.
|
||||
CellBorder = "cell-border"
|
||||
|
||||
// CellBorderLeft is the constant for the "cell-border-left" property tag.
|
||||
// The "cell-border-left" property sets a view's left border. It sets the values of a border width, style, and color.
|
||||
// This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation.
|
||||
CellBorderLeft = "cell-border-left"
|
||||
|
||||
// CellBorderRight is the constant for the "cell-border-right" property tag.
|
||||
// The "cell-border-right" property sets a view's right border. It sets the values of a border width, style, and color.
|
||||
// This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation.
|
||||
CellBorderRight = "cell-border-right"
|
||||
|
||||
// CellBorderTop is the constant for the "cell-border-top" property tag.
|
||||
// The "cell-border-top" property sets a view's top border. It sets the values of a border width, style, and color.
|
||||
// This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation.
|
||||
CellBorderTop = "cell-border-top"
|
||||
|
||||
// CellBorderBottom is the constant for the "cell-border-bottom" property tag.
|
||||
// The "cell-border-bottom" property sets a view's bottom border. It sets the values of a border width, style, and color.
|
||||
// This property can be assigned a value of BorderProperty type, or ViewBorder type, or BorderProperty text representation.
|
||||
CellBorderBottom = "cell-border-bottom"
|
||||
|
||||
// CellBorderStyle is the constant for the "cell-border-style" property tag.
|
||||
// The "cell-border-style" int property sets the line style for all four sides of a table cell's border.
|
||||
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4).
|
||||
CellBorderStyle = "cell-border-style"
|
||||
|
||||
// CellBorderLeftStyle is the constant for the "cell-border-left-style" property tag.
|
||||
// The "cell-border-left-style" int property sets the line style of a table cell's left border.
|
||||
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4).
|
||||
CellBorderLeftStyle = "cell-border-left-style"
|
||||
|
||||
// CellBorderRightStyle is the constant for the "cell-border-right-style" property tag.
|
||||
// The "cell-border-right-style" int property sets the line style of a table cell's right border.
|
||||
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4).
|
||||
CellBorderRightStyle = "cell-border-right-style"
|
||||
|
||||
// CellBorderTopStyle is the constant for the "cell-border-top-style" property tag.
|
||||
// The "cell-border-top-style" int property sets the line style of a table cell's top border.
|
||||
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4).
|
||||
CellBorderTopStyle = "cell-border-top-style"
|
||||
|
||||
// CellBorderBottomStyle is the constant for the "cell-border-bottom-style" property tag.
|
||||
// The "cell-border-bottom-style" int property sets the line style of a table cell's bottom border.
|
||||
// Valid values are NoneLine (0), SolidLine (1), DashedLine (2), DottedLine (3), and DoubleLine (4).
|
||||
CellBorderBottomStyle = "cell-border-bottom-style"
|
||||
|
||||
// CellBorderWidth is the constant for the "cell-border-width" property tag.
|
||||
// The "cell-border-width" property sets the line width for all four sides of a table cell's border.
|
||||
CellBorderWidth = "cell-border-width"
|
||||
|
||||
// CellBorderLeftWidth is the constant for the "cell-border-left-width" property tag.
|
||||
// The "cell-border-left-width" SizeUnit property sets the line width of a table cell's left border.
|
||||
CellBorderLeftWidth = "cell-border-left-width"
|
||||
|
||||
// CellBorderRightWidth is the constant for the "cell-border-right-width" property tag.
|
||||
// The "cell-border-right-width" SizeUnit property sets the line width of a table cell's right border.
|
||||
CellBorderRightWidth = "cell-border-right-width"
|
||||
|
||||
// CellBorderTopWidth is the constant for the "cell-border-top-width" property tag.
|
||||
// The "cell-border-top-width" SizeUnit property sets the line width of a table cell's top border.
|
||||
CellBorderTopWidth = "cell-border-top-width"
|
||||
|
||||
// CellBorderBottomWidth is the constant for the "cell-border-bottom-width" property tag.
|
||||
// The "cell-border-bottom-width" SizeUnit property sets the line width of a table cell's bottom border.
|
||||
CellBorderBottomWidth = "cell-border-bottom-width"
|
||||
|
||||
// CellBorderColor is the constant for the "cell-border-color" property tag.
|
||||
// The "cell-border-color" property sets the line color for all four sides of a table cell's border.
|
||||
CellBorderColor = "cell-border-color"
|
||||
|
||||
// CellBorderLeftColor is the constant for the "cell-border-left-color" property tag.
|
||||
// The "cell-border-left-color" property sets the line color of a table cell's left border.
|
||||
CellBorderLeftColor = "cell-border-left-color"
|
||||
|
||||
// CellBorderRightColor is the constant for the "cell-border-right-color" property tag.
|
||||
// The "cell-border-right-color" property sets the line color of a table cell's right border.
|
||||
CellBorderRightColor = "cell-border-right-color"
|
||||
|
||||
// CellBorderTopColor is the constant for the "cell-border-top-color" property tag.
|
||||
// The "cell-border-top-color" property sets the line color of a table cell's top border.
|
||||
CellBorderTopColor = "cell-border-top-color"
|
||||
|
||||
// CellBorderBottomColor is the constant for the "cell-border-bottom-color" property tag.
|
||||
// The "cell-border-bottom-color" property sets the line color of a table cell's bottom border.
|
||||
CellBorderBottomColor = "cell-border-bottom-color"
|
||||
|
||||
// SelectionMode is the constant for the "selection-mode" property tag.
|
||||
// The "selection-mode" int property sets the mode of the table elements selection.
|
||||
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2)
|
||||
SelectionMode = "selection-mode"
|
||||
|
||||
// TableCellClickedEvent is the constant for "table-cell-clicked" property tag.
|
||||
// The "table-cell-clicked" event occurs when the user clicks on a table cell.
|
||||
// The main listener format: func(TableView, int, int), where the second argument is the row number,
|
||||
// and third argument is the column number.
|
||||
TableCellClickedEvent = "table-cell-clicked"
|
||||
|
||||
// TableCellSelectedEvent is the constant for "table-cell-selected" property tag.
|
||||
// The "table-cell-selected" event occurs when a table cell becomes selected.
|
||||
// The main listener format: func(TableView, int, int), where the second argument is the row number,
|
||||
// and third argument is the column number.
|
||||
TableCellSelectedEvent = "table-cell-selected"
|
||||
|
||||
// TableRowClickedEvent is the constant for "table-row-clicked" property tag.
|
||||
// The "table-row-clicked" event occurs when the user clicks on a table row.
|
||||
// The main listener format: func(TableView, int), where the second argument is the row number.
|
||||
TableRowClickedEvent = "table-row-clicked"
|
||||
|
||||
// TableRowSelectedEvent is the constant for "table-row-selected" property tag.
|
||||
// The "table-row-selected" event occurs when a table row becomes selected.
|
||||
// The main listener format: func(TableView, int), where the second argument is the row number.
|
||||
TableRowSelectedEvent = "table-row-selected"
|
||||
|
||||
// AllowSelection is the constant for the "allow-selection" property tag.
|
||||
// The "allow-selection" property sets the adapter which specifies styles of each table row.
|
||||
// This property can be assigned or by an implementation of TableAllowCellSelection
|
||||
// or TableAllowRowSelection interface.
|
||||
AllowSelection = "allow-selection"
|
||||
|
||||
// NoneSelection is the value of "selection-mode" property: the selection is forbidden.
|
||||
NoneSelection = 0
|
||||
// CellSelection is the value of "selection-mode" property: the selection of a single cell only is enabled.
|
||||
CellSelection = 1
|
||||
// RowSelection is the value of "selection-mode" property: the selection of a table row only is enabled.
|
||||
RowSelection = 2
|
||||
)
|
||||
|
||||
// CellIndex defines coordinates of the TableView cell
|
||||
type CellIndex struct {
|
||||
Row, Column int
|
||||
}
|
||||
|
||||
// TableView - text View
|
||||
type TableView interface {
|
||||
View
|
||||
ParanetView
|
||||
ReloadTableData()
|
||||
CellFrame(row, column int) Frame
|
||||
|
||||
content() TableAdapter
|
||||
getCurrent() CellIndex
|
||||
getRowStyle() TableRowStyle
|
||||
getColumnStyle() TableColumnStyle
|
||||
getCellStyle() TableCellStyle
|
||||
}
|
||||
|
||||
type tableViewData struct {
|
||||
viewData
|
||||
cellViews []View
|
||||
cellFrame []Frame
|
||||
cellSelectedListener, cellClickedListener []func(TableView, int, int)
|
||||
rowSelectedListener, rowClickedListener []func(TableView, int)
|
||||
current CellIndex
|
||||
}
|
||||
|
||||
type tableCellView struct {
|
||||
|
@ -169,6 +259,13 @@ func (table *tableViewData) Init(session Session) {
|
|||
table.viewData.Init(session)
|
||||
table.tag = "TableView"
|
||||
table.cellViews = []View{}
|
||||
table.cellFrame = []Frame{}
|
||||
table.cellSelectedListener = []func(TableView, int, int){}
|
||||
table.cellClickedListener = []func(TableView, int, int){}
|
||||
table.rowSelectedListener = []func(TableView, int){}
|
||||
table.rowClickedListener = []func(TableView, int){}
|
||||
table.current.Row = -1
|
||||
table.current.Column = -1
|
||||
}
|
||||
|
||||
func (table *tableViewData) normalizeTag(tag string) string {
|
||||
|
@ -188,6 +285,10 @@ func (table *tableViewData) normalizeTag(tag string) string {
|
|||
return tag
|
||||
}
|
||||
|
||||
func (table *tableViewData) Focusable() bool {
|
||||
return GetTableSelectionMode(table, "") != NoneSelection
|
||||
}
|
||||
|
||||
func (table *tableViewData) Get(tag string) interface{} {
|
||||
return table.get(table.normalizeTag(tag))
|
||||
}
|
||||
|
@ -202,13 +303,34 @@ func (table *tableViewData) remove(tag string) {
|
|||
table.removeBoundsSide(CellPadding, tag)
|
||||
table.propertyChanged(tag)
|
||||
|
||||
case Gap, CellBorder, CellPadding, RowStyle, ColumnStyle, CellStyle,
|
||||
HeadHeight, HeadStyle, FootHeight, FootStyle:
|
||||
case SelectionMode, TableVerticalAlign, Gap, CellBorder, CellPadding, RowStyle,
|
||||
ColumnStyle, CellStyle, HeadHeight, HeadStyle, FootHeight, FootStyle, AllowSelection:
|
||||
if _, ok := table.properties[tag]; ok {
|
||||
delete(table.properties, tag)
|
||||
table.propertyChanged(tag)
|
||||
}
|
||||
|
||||
case TableCellClickedEvent:
|
||||
table.cellClickedListener = []func(TableView, int, int){}
|
||||
table.propertyChanged(tag)
|
||||
|
||||
case TableCellSelectedEvent:
|
||||
table.cellSelectedListener = []func(TableView, int, int){}
|
||||
table.propertyChanged(tag)
|
||||
|
||||
case TableRowClickedEvent:
|
||||
table.rowClickedListener = []func(TableView, int){}
|
||||
table.propertyChanged(tag)
|
||||
|
||||
case TableRowSelectedEvent:
|
||||
table.rowSelectedListener = []func(TableView, int){}
|
||||
table.propertyChanged(tag)
|
||||
|
||||
case Current:
|
||||
table.current.Row = -1
|
||||
table.current.Column = -1
|
||||
table.propertyChanged(tag)
|
||||
|
||||
default:
|
||||
table.viewData.remove(tag)
|
||||
}
|
||||
|
@ -241,6 +363,38 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
case TableCellClickedEvent:
|
||||
listeners := table.valueToCellListeners(value)
|
||||
if listeners == nil {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
table.cellClickedListener = listeners
|
||||
|
||||
case TableCellSelectedEvent:
|
||||
listeners := table.valueToCellListeners(value)
|
||||
if listeners == nil {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
table.cellSelectedListener = listeners
|
||||
|
||||
case TableRowClickedEvent:
|
||||
listeners := table.valueToRowListeners(value)
|
||||
if listeners == nil {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
table.rowClickedListener = listeners
|
||||
|
||||
case TableRowSelectedEvent:
|
||||
listeners := table.valueToRowListeners(value)
|
||||
if listeners == nil {
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
table.rowSelectedListener = []func(TableView, int){}
|
||||
|
||||
case CellStyle:
|
||||
if style, ok := value.(TableCellStyle); ok {
|
||||
table.properties[tag] = style
|
||||
|
@ -336,7 +490,7 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
case CellBorder, CellBorderStyle, CellBorderColor, CellBorderWidth,
|
||||
case SelectionMode, TableVerticalAlign, CellBorder, CellBorderStyle, CellBorderColor, CellBorderWidth,
|
||||
CellBorderLeft, CellBorderLeftStyle, CellBorderLeftColor, CellBorderLeftWidth,
|
||||
CellBorderRight, CellBorderRightStyle, CellBorderRightColor, CellBorderRightWidth,
|
||||
CellBorderTop, CellBorderTopStyle, CellBorderTopColor, CellBorderTopWidth,
|
||||
|
@ -345,6 +499,67 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
case AllowSelection:
|
||||
switch value.(type) {
|
||||
case TableAllowCellSelection:
|
||||
table.properties[tag] = value
|
||||
|
||||
case TableAllowRowSelection:
|
||||
table.properties[tag] = value
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
case Current:
|
||||
switch value := value.(type) {
|
||||
case int:
|
||||
table.current.Row = value
|
||||
table.current.Column = -1
|
||||
|
||||
case CellIndex:
|
||||
table.current = value
|
||||
|
||||
case DataObject:
|
||||
if row, ok := dataIntProperty(value, "row"); ok {
|
||||
table.current.Row = row
|
||||
}
|
||||
if column, ok := dataIntProperty(value, "column"); ok {
|
||||
table.current.Column = column
|
||||
}
|
||||
|
||||
case string:
|
||||
if strings.Contains(value, ",") {
|
||||
if values := strings.Split(value, ","); len(values) == 2 {
|
||||
var n = []int{0, 0}
|
||||
for i := 0; i < 2; i++ {
|
||||
var err error
|
||||
if n[i], err = strconv.Atoi(values[i]); err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
}
|
||||
}
|
||||
table.current.Row = n[0]
|
||||
table.current.Column = n[1]
|
||||
} else {
|
||||
notCompatibleType(tag, value)
|
||||
}
|
||||
} else {
|
||||
n, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
ErrorLog(err.Error())
|
||||
return false
|
||||
}
|
||||
table.current.Row = n
|
||||
table.current.Column = -1
|
||||
}
|
||||
|
||||
default:
|
||||
notCompatibleType(tag, value)
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
return table.viewData.set(tag, value)
|
||||
}
|
||||
|
@ -356,9 +571,11 @@ func (table *tableViewData) set(tag string, value interface{}) bool {
|
|||
func (table *tableViewData) propertyChanged(tag string) {
|
||||
if table.created {
|
||||
switch tag {
|
||||
case Content, RowStyle, ColumnStyle, CellStyle, CellPadding, CellBorder,
|
||||
HeadHeight, HeadStyle, FootHeight, FootStyle,
|
||||
CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft:
|
||||
case Content, TableVerticalAlign, RowStyle, ColumnStyle, CellStyle, CellPadding,
|
||||
CellBorder, HeadHeight, HeadStyle, FootHeight, FootStyle,
|
||||
CellPaddingTop, CellPaddingRight, CellPaddingBottom, CellPaddingLeft,
|
||||
TableCellClickedEvent, TableCellSelectedEvent, TableRowClickedEvent,
|
||||
TableRowSelectedEvent, AllowSelection:
|
||||
table.ReloadTableData()
|
||||
|
||||
case Gap:
|
||||
|
@ -372,25 +589,252 @@ func (table *tableViewData) propertyChanged(tag string) {
|
|||
updateCSSProperty(htmlID, "border-spacing", gap.cssString("0"), session)
|
||||
updateCSSProperty(htmlID, "border-collapse", "separate", session)
|
||||
}
|
||||
|
||||
case SelectionMode:
|
||||
htmlID := table.htmlID()
|
||||
session := table.Session()
|
||||
|
||||
switch GetTableSelectionMode(table, "") {
|
||||
case CellSelection:
|
||||
updateProperty(htmlID, "tabindex", "0", session)
|
||||
updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session)
|
||||
updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)", session)
|
||||
updateProperty(htmlID, "data-selection", "cell", session)
|
||||
updateProperty(htmlID, "data-focusitemstyle", table.currentStyle(), session)
|
||||
updateProperty(htmlID, "data-bluritemstyle", table.currentInactiveStyle(), session)
|
||||
|
||||
if table.current.Row >= 0 && table.current.Column >= 0 {
|
||||
updateProperty(htmlID, "data-current", table.cellID(table.current.Row, table.current.Column), session)
|
||||
} else {
|
||||
removeProperty(htmlID, "data-current", session)
|
||||
}
|
||||
updateProperty(htmlID, "onkeydown", "tableViewCellKeyDownEvent(this, event)", session)
|
||||
|
||||
case RowSelection:
|
||||
updateProperty(htmlID, "tabindex", "0", session)
|
||||
updateProperty(htmlID, "onfocus", "tableViewFocusEvent(this, event)", session)
|
||||
updateProperty(htmlID, "onblur", "tableViewBlurEvent(this, event)", session)
|
||||
updateProperty(htmlID, "data-selection", "cell", session)
|
||||
updateProperty(htmlID, "data-focusitemstyle", table.currentStyle(), session)
|
||||
updateProperty(htmlID, "data-bluritemstyle", table.currentInactiveStyle(), session)
|
||||
|
||||
if table.current.Row >= 0 {
|
||||
updateProperty(htmlID, "data-current", table.rowID(table.current.Row), session)
|
||||
} else {
|
||||
removeProperty(htmlID, "data-current", session)
|
||||
}
|
||||
updateProperty(htmlID, "onkeydown", "tableViewRowKeyDownEvent(this, event)", session)
|
||||
|
||||
default: // NoneSelection
|
||||
for _, prop := range []string{"tabindex", "data-current", "onfocus", "onblur", "onkeydown", "data-selection"} {
|
||||
removeProperty(htmlID, prop, session)
|
||||
}
|
||||
}
|
||||
updateInnerHTML(htmlID, session)
|
||||
}
|
||||
}
|
||||
table.propertyChangedEvent(tag)
|
||||
}
|
||||
|
||||
func (table *tableViewData) currentStyle() string {
|
||||
if value := table.getRaw(CurrentStyle); value != nil {
|
||||
if style, ok := value.(string); ok {
|
||||
if style, ok = table.session.resolveConstants(style); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return "ruiCurrentTableCellFocused"
|
||||
}
|
||||
|
||||
func (table *tableViewData) currentInactiveStyle() string {
|
||||
if value := table.getRaw(CurrentInactiveStyle); value != nil {
|
||||
if style, ok := value.(string); ok {
|
||||
if style, ok = table.session.resolveConstants(style); ok {
|
||||
return style
|
||||
}
|
||||
}
|
||||
}
|
||||
return "ruiCurrentTableCell"
|
||||
}
|
||||
|
||||
func (table *tableViewData) valueToCellListeners(value interface{}) []func(TableView, int, int) {
|
||||
if value == nil {
|
||||
return []func(TableView, int, int){}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(TableView, int, int):
|
||||
return []func(TableView, int, int){value}
|
||||
|
||||
case func(int, int):
|
||||
fn := func(view TableView, row, column int) {
|
||||
value(row, column)
|
||||
}
|
||||
return []func(TableView, int, int){fn}
|
||||
|
||||
case []func(TableView, int, int):
|
||||
return value
|
||||
|
||||
case []func(int, int):
|
||||
listeners := make([]func(TableView, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(view TableView, row, column int) {
|
||||
val(row, column)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []interface{}:
|
||||
listeners := make([]func(TableView, int, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case func(TableView, int, int):
|
||||
listeners[i] = val
|
||||
|
||||
case func(int, int):
|
||||
listeners[i] = func(view TableView, row, column int) {
|
||||
val(row, column)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) valueToRowListeners(value interface{}) []func(TableView, int) {
|
||||
if value == nil {
|
||||
return []func(TableView, int){}
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case func(TableView, int):
|
||||
return []func(TableView, int){value}
|
||||
|
||||
case func(int):
|
||||
fn := func(view TableView, index int) {
|
||||
value(index)
|
||||
}
|
||||
return []func(TableView, int){fn}
|
||||
|
||||
case []func(TableView, int):
|
||||
return value
|
||||
|
||||
case []func(int):
|
||||
listeners := make([]func(TableView, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
listeners[i] = func(view TableView, index int) {
|
||||
val(index)
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
|
||||
case []interface{}:
|
||||
listeners := make([]func(TableView, int), len(value))
|
||||
for i, val := range value {
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := val.(type) {
|
||||
case func(TableView, int):
|
||||
listeners[i] = val
|
||||
|
||||
case func(int):
|
||||
listeners[i] = func(view TableView, index int) {
|
||||
val(index)
|
||||
}
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return listeners
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) htmlTag() string {
|
||||
return "table"
|
||||
}
|
||||
|
||||
func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
table.cellViews = []View{}
|
||||
func (table *tableViewData) rowID(index int) string {
|
||||
return fmt.Sprintf("%s-%d", table.htmlID(), index)
|
||||
}
|
||||
|
||||
content := table.getRaw(Content)
|
||||
if content == nil {
|
||||
return
|
||||
func (table *tableViewData) cellID(row, column int) string {
|
||||
return fmt.Sprintf("%s-%d-%d", table.htmlID(), row, column)
|
||||
}
|
||||
|
||||
func (table *tableViewData) htmlProperties(self View, buffer *strings.Builder) {
|
||||
|
||||
if content := table.content(); content != nil {
|
||||
buffer.WriteString(` data-rows="`)
|
||||
buffer.WriteString(strconv.Itoa(content.RowCount()))
|
||||
buffer.WriteString(`" data-columns="`)
|
||||
buffer.WriteString(strconv.Itoa(content.ColumnCount()))
|
||||
buffer.WriteRune('"')
|
||||
}
|
||||
|
||||
adapter, ok := content.(TableAdapter)
|
||||
if !ok {
|
||||
if selectionMode := GetTableSelectionMode(table, ""); selectionMode != NoneSelection {
|
||||
buffer.WriteString(` onfocus="tableViewFocusEvent(this, event)" onblur="tableViewBlurEvent(this, event)" data-focusitemstyle="`)
|
||||
buffer.WriteString(table.currentStyle())
|
||||
buffer.WriteString(`" data-bluritemstyle="`)
|
||||
buffer.WriteString(table.currentInactiveStyle())
|
||||
buffer.WriteRune('"')
|
||||
|
||||
switch selectionMode {
|
||||
case RowSelection:
|
||||
buffer.WriteString(` data-selection="row" onkeydown="tableViewRowKeyDownEvent(this, event)"`)
|
||||
if table.current.Row >= 0 {
|
||||
buffer.WriteString(` data-current="`)
|
||||
buffer.WriteString(table.rowID(table.current.Row))
|
||||
buffer.WriteRune('"')
|
||||
}
|
||||
|
||||
case CellSelection:
|
||||
buffer.WriteString(` data-selection="cell" onkeydown="tableViewCellKeyDownEvent(this, event)"`)
|
||||
if table.current.Row >= 0 && table.current.Column >= 0 {
|
||||
buffer.WriteString(` data-current="`)
|
||||
buffer.WriteString(table.cellID(table.current.Row, table.current.Column))
|
||||
buffer.WriteRune('"')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.viewData.htmlProperties(self, buffer)
|
||||
}
|
||||
|
||||
func (table *tableViewData) content() TableAdapter {
|
||||
if content := table.getRaw(Content); content != nil {
|
||||
if adapter, ok := content.(TableAdapter); ok {
|
||||
return adapter
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
||||
table.cellViews = []View{}
|
||||
table.cellFrame = []Frame{}
|
||||
|
||||
adapter := table.content()
|
||||
if adapter == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -400,19 +844,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
return
|
||||
}
|
||||
|
||||
table.cellFrame = make([]Frame, rowCount*columnCount)
|
||||
|
||||
rowStyle := table.getRowStyle()
|
||||
|
||||
var cellStyle1 TableCellStyle = nil
|
||||
if style, ok := content.(TableCellStyle); ok {
|
||||
cellStyle1 = style
|
||||
}
|
||||
|
||||
var cellStyle2 TableCellStyle = nil
|
||||
if value := table.getRaw(CellStyle); value != nil {
|
||||
if style, ok := value.(TableCellStyle); ok {
|
||||
cellStyle2 = style
|
||||
}
|
||||
}
|
||||
cellStyle := table.getCellStyle()
|
||||
|
||||
session := table.Session()
|
||||
|
||||
|
@ -429,6 +864,35 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
view.Init(session)
|
||||
|
||||
ignorCells := []struct{ row, column int }{}
|
||||
selectionMode := GetTableSelectionMode(table, "")
|
||||
|
||||
var allowCellSelection TableAllowCellSelection = nil
|
||||
if allow, ok := adapter.(TableAllowCellSelection); ok {
|
||||
allowCellSelection = allow
|
||||
}
|
||||
if value := table.getRaw(AllowSelection); value != nil {
|
||||
if style, ok := value.(TableAllowCellSelection); ok {
|
||||
allowCellSelection = style
|
||||
}
|
||||
}
|
||||
|
||||
var allowRowSelection TableAllowRowSelection = nil
|
||||
if allow, ok := adapter.(TableAllowRowSelection); ok {
|
||||
allowRowSelection = allow
|
||||
}
|
||||
if value := table.getRaw(AllowSelection); value != nil {
|
||||
if style, ok := value.(TableAllowRowSelection); ok {
|
||||
allowRowSelection = style
|
||||
}
|
||||
}
|
||||
|
||||
vAlignCss := enumProperties[TableVerticalAlign].cssValues
|
||||
vAlignValue := GetTableVerticalAlign(table, "")
|
||||
if vAlignValue < 0 || vAlignValue >= len(vAlignCss) {
|
||||
vAlignValue = 0
|
||||
}
|
||||
|
||||
vAlign := vAlignCss[vAlignValue]
|
||||
|
||||
tableCSS := func(startRow, endRow int, cellTag string, cellBorder BorderProperty, cellPadding BoundsProperty) {
|
||||
for row := startRow; row < endRow; row++ {
|
||||
|
@ -444,13 +908,34 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
|
||||
if cssBuilder.buffer.Len() > 0 {
|
||||
buffer.WriteString(`<tr style="`)
|
||||
buffer.WriteString(cssBuilder.buffer.String())
|
||||
buffer.WriteString(`">`)
|
||||
buffer.WriteString(`<tr id="`)
|
||||
buffer.WriteString(table.rowID(row))
|
||||
buffer.WriteRune('"')
|
||||
|
||||
if selectionMode == RowSelection {
|
||||
if row == table.current.Row {
|
||||
buffer.WriteString(` class="`)
|
||||
if table.HasFocus() {
|
||||
buffer.WriteString(table.currentStyle())
|
||||
} else {
|
||||
buffer.WriteString("<tr>")
|
||||
buffer.WriteString(table.currentInactiveStyle())
|
||||
}
|
||||
buffer.WriteRune('"')
|
||||
}
|
||||
|
||||
buffer.WriteString(` onclick="tableRowClickEvent(this, event)"`)
|
||||
|
||||
if allowRowSelection != nil && !allowRowSelection.AllowRowSelection(row) {
|
||||
buffer.WriteString(` data-disabled="1"`)
|
||||
}
|
||||
}
|
||||
|
||||
if cssBuilder.buffer.Len() > 0 {
|
||||
buffer.WriteString(` style="`)
|
||||
buffer.WriteString(cssBuilder.buffer.String())
|
||||
buffer.WriteString(`"`)
|
||||
}
|
||||
buffer.WriteString(">")
|
||||
|
||||
for column := 0; column < columnCount; column++ {
|
||||
ignore := false
|
||||
|
@ -476,7 +961,6 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
view.set(Padding, cellPadding)
|
||||
}
|
||||
|
||||
appendFrom := func(cellStyle TableCellStyle) {
|
||||
if cellStyle != nil {
|
||||
if styles := cellStyle.CellStyle(row, column); styles != nil {
|
||||
for tag, value := range styles {
|
||||
|
@ -486,7 +970,7 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
return value
|
||||
|
||||
case string:
|
||||
if value, ok = session.resolveConstants(value); ok {
|
||||
if value, ok := session.resolveConstants(value); ok {
|
||||
if n, err := strconv.Atoi(value); err == nil {
|
||||
return n
|
||||
}
|
||||
|
@ -508,9 +992,6 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
appendFrom(cellStyle1)
|
||||
appendFrom(cellStyle2)
|
||||
|
||||
if len(view.properties) > 0 {
|
||||
view.cssStyle(&view, &cssBuilder)
|
||||
|
@ -518,6 +999,26 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
|
||||
buffer.WriteRune('<')
|
||||
buffer.WriteString(cellTag)
|
||||
buffer.WriteString(` id="`)
|
||||
buffer.WriteString(table.cellID(row, column))
|
||||
buffer.WriteString(`" class="ruiView`)
|
||||
|
||||
if selectionMode == CellSelection && row == table.current.Row && column == table.current.Column {
|
||||
buffer.WriteRune(' ')
|
||||
if table.HasFocus() {
|
||||
buffer.WriteString(table.currentStyle())
|
||||
} else {
|
||||
buffer.WriteString(table.currentInactiveStyle())
|
||||
}
|
||||
}
|
||||
buffer.WriteRune('"')
|
||||
|
||||
if selectionMode == CellSelection {
|
||||
buffer.WriteString(` onclick="tableCellClickEvent(this, event)"`)
|
||||
if allowCellSelection != nil && !allowCellSelection.AllowCellSelection(row, column) {
|
||||
buffer.WriteString(` data-disabled="1"`)
|
||||
}
|
||||
}
|
||||
|
||||
if columnSpan > 1 {
|
||||
buffer.WriteString(` colspan="`)
|
||||
|
@ -629,8 +1130,8 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
buffer.WriteString("</colgroup>")
|
||||
}
|
||||
|
||||
headHeight, _ := intProperty(table, HeadHeight, table.Session(), 0)
|
||||
footHeight, _ := intProperty(table, FootHeight, table.Session(), 0)
|
||||
headHeight := GetTableHeadHeight(table, "")
|
||||
footHeight := GetTableFootHeight(table, "")
|
||||
cellBorder := table.getCellBorder()
|
||||
cellPadding := table.boundsProperty(CellPadding)
|
||||
if cellPadding == nil {
|
||||
|
@ -650,13 +1151,17 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
if style, ok := session.resolveConstants(value); ok {
|
||||
buffer.WriteString(` class="`)
|
||||
buffer.WriteString(style)
|
||||
buffer.WriteString(`">`)
|
||||
buffer.WriteString(`" style="vertical-align: `)
|
||||
buffer.WriteString(vAlign)
|
||||
buffer.WriteString(`;">`)
|
||||
|
||||
return table.cellBorderFromStyle(style), table.cellPaddingFromStyle(style)
|
||||
}
|
||||
|
||||
case Params:
|
||||
cssBuilder.buffer.Reset()
|
||||
view.Clear()
|
||||
view.Set(TableVerticalAlign, vAlignValue)
|
||||
for tag, val := range value {
|
||||
view.Set(tag, val)
|
||||
}
|
||||
|
@ -691,7 +1196,10 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
return border, padding
|
||||
}
|
||||
}
|
||||
buffer.WriteRune('>')
|
||||
|
||||
buffer.WriteString(` style="vertical-align: `)
|
||||
buffer.WriteString(vAlign)
|
||||
buffer.WriteString(`;">`)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -719,7 +1227,9 @@ func (table *tableViewData) htmlSubviews(self View, buffer *strings.Builder) {
|
|||
}
|
||||
|
||||
if rowCount > footHeight+headHeight {
|
||||
buffer.WriteString("<tbody>")
|
||||
buffer.WriteString(`<tbody style="vertical-align: `)
|
||||
buffer.WriteString(vAlign)
|
||||
buffer.WriteString(`;">`)
|
||||
tableCSS(headHeight, rowCount-footHeight, "td", cellBorder, cellPadding)
|
||||
buffer.WriteString("</tbody>")
|
||||
}
|
||||
|
@ -825,6 +1335,10 @@ func (table *tableViewData) getCellBorder() BorderProperty {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (table *tableViewData) getCurrent() CellIndex {
|
||||
return table.current
|
||||
}
|
||||
|
||||
func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
|
||||
table.viewData.cssViewStyle(builder, table.Session())
|
||||
|
||||
|
@ -839,30 +1353,93 @@ func (table *tableViewData) cssStyle(self View, builder cssBuilder) {
|
|||
}
|
||||
|
||||
func (table *tableViewData) ReloadTableData() {
|
||||
if content := table.content(); content != nil {
|
||||
updateProperty(table.htmlID(), "data-rows", strconv.Itoa(content.RowCount()), table.Session())
|
||||
updateProperty(table.htmlID(), "data-columns", strconv.Itoa(content.ColumnCount()), table.Session())
|
||||
}
|
||||
updateInnerHTML(table.htmlID(), table.Session())
|
||||
}
|
||||
|
||||
func (cell *tableCellView) Set(tag string, value interface{}) bool {
|
||||
return cell.set(strings.ToLower(tag), value)
|
||||
func (table *tableViewData) onItemResize(self View, index string, x, y, width, height float64) {
|
||||
if n := strings.IndexRune(index, '-'); n > 0 {
|
||||
if row, err := strconv.Atoi(index[:n]); err == nil {
|
||||
if column, err := strconv.Atoi(index[n+1:]); err == nil {
|
||||
if content := table.content(); content != nil {
|
||||
i := row*content.ColumnCount() + column
|
||||
if i < len(table.cellFrame) {
|
||||
table.cellFrame[i].Left = x
|
||||
table.cellFrame[i].Top = y
|
||||
table.cellFrame[i].Width = width
|
||||
table.cellFrame[i].Height = height
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ErrorLog(err.Error())
|
||||
}
|
||||
} else {
|
||||
ErrorLog(err.Error())
|
||||
}
|
||||
} else {
|
||||
ErrorLogF(`Invalid cell index: %s`, index)
|
||||
}
|
||||
}
|
||||
|
||||
func (cell *tableCellView) set(tag string, value interface{}) bool {
|
||||
switch tag {
|
||||
case VerticalAlign:
|
||||
tag = TableVerticalAlign
|
||||
func (table *tableViewData) CellFrame(row, column int) Frame {
|
||||
if content := table.content(); content != nil {
|
||||
i := row*content.ColumnCount() + column
|
||||
if i < len(table.cellFrame) {
|
||||
return table.cellFrame[i]
|
||||
}
|
||||
return cell.viewData.set(tag, value)
|
||||
}
|
||||
|
||||
func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
||||
session := cell.Session()
|
||||
cell.viewData.cssViewStyle(builder, session)
|
||||
|
||||
if value, ok := enumProperty(cell, TableVerticalAlign, session, 0); ok {
|
||||
builder.add("vertical-align", enumProperties[TableVerticalAlign].values[value])
|
||||
}
|
||||
return Frame{}
|
||||
}
|
||||
|
||||
func (table *tableViewData) Views() []View {
|
||||
return table.cellViews
|
||||
}
|
||||
|
||||
func (table *tableViewData) handleCommand(self View, command string, data DataObject) bool {
|
||||
switch command {
|
||||
case "currentRow":
|
||||
if row, ok := dataIntProperty(data, "row"); ok && row != table.current.Row {
|
||||
table.current.Row = row
|
||||
for _, listener := range table.rowSelectedListener {
|
||||
listener(table, row)
|
||||
}
|
||||
}
|
||||
|
||||
case "currentCell":
|
||||
if row, ok := dataIntProperty(data, "row"); ok {
|
||||
if column, ok := dataIntProperty(data, "column"); ok {
|
||||
if row != table.current.Row || column != table.current.Column {
|
||||
table.current.Row = row
|
||||
table.current.Column = column
|
||||
for _, listener := range table.cellSelectedListener {
|
||||
listener(table, row, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case "rowClick":
|
||||
if row, ok := dataIntProperty(data, "row"); ok {
|
||||
for _, listener := range table.rowClickedListener {
|
||||
listener(table, row)
|
||||
}
|
||||
}
|
||||
|
||||
case "cellClick":
|
||||
if row, ok := dataIntProperty(data, "row"); ok {
|
||||
if column, ok := dataIntProperty(data, "column"); ok {
|
||||
for _, listener := range table.cellClickedListener {
|
||||
listener(table, row, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return table.viewData.handleCommand(self, command, data)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
package rui
|
||||
|
||||
import "strings"
|
||||
|
||||
func (cell *tableCellView) Set(tag string, value interface{}) bool {
|
||||
return cell.set(strings.ToLower(tag), value)
|
||||
}
|
||||
|
||||
func (cell *tableCellView) set(tag string, value interface{}) bool {
|
||||
switch tag {
|
||||
case VerticalAlign:
|
||||
tag = TableVerticalAlign
|
||||
}
|
||||
return cell.viewData.set(tag, value)
|
||||
}
|
||||
|
||||
func (cell *tableCellView) cssStyle(self View, builder cssBuilder) {
|
||||
session := cell.Session()
|
||||
cell.viewData.cssViewStyle(builder, session)
|
||||
|
||||
if value, ok := enumProperty(cell, TableVerticalAlign, session, 0); ok {
|
||||
builder.add("vertical-align", enumProperties[TableVerticalAlign].values[value])
|
||||
}
|
||||
}
|
||||
|
||||
// GetTableContent returns a TableAdapter which defines the TableView content.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableContent(view View, subviewID string) TableAdapter {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.content()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTableRowStyle returns a TableRowStyle which defines styles of TableView rows.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableRowStyle(view View, subviewID string) TableRowStyle {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getRowStyle()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTableColumnStyle returns a TableColumnStyle which defines styles of TableView columns.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableColumnStyle(view View, subviewID string) TableColumnStyle {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getColumnStyle()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTableCellStyle returns a TableCellStyle which defines styles of TableView cells.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCellStyle(view View, subviewID string) TableCellStyle {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
|
||||
if view != nil {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getCellStyle()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTableSelectionMode returns the mode of the TableView elements selection.
|
||||
// Valid values are NoneSelection (0), CellSelection (1), and RowSelection (2).
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableSelectionMode(view View, subviewID string) int {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
if result, ok := enumStyledProperty(view, SelectionMode, NoneSelection); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return NoneSelection
|
||||
}
|
||||
|
||||
// GetTableVerticalAlign returns a vertical align in a TavleView cell. Returns one of next values:
|
||||
// TopAlign (0), BottomAlign (1), CenterAlign (2), and BaselineAlign (3)
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableVerticalAlign(view View, subviewID string) int {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
if result, ok := enumStyledProperty(view, TableVerticalAlign, TopAlign); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return TopAlign
|
||||
}
|
||||
|
||||
// GetTableHeadHeight returns the number of rows in the table header.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableHeadHeight(view View, subviewID string) int {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
headHeight, _ := intStyledProperty(view, HeadHeight, 0)
|
||||
return headHeight
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetTableFootHeight returns the number of rows in the table footer.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableFootHeight(view View, subviewID string) int {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
headHeight, _ := intStyledProperty(view, FootHeight, 0)
|
||||
return headHeight
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// GetTableCurrent returns the row and column index of the TableView selected cell/row.
|
||||
// If there is no selected cell/row or the selection mode is NoneSelection (0),
|
||||
// then a value of the row and column index less than 0 is returned.
|
||||
// If the selection mode is RowSelection (2) then the returned column index is less than 0.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCurrent(view View, subviewID string) CellIndex {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
|
||||
if view != nil {
|
||||
if selectionMode := GetTableSelectionMode(view, ""); selectionMode != NoneSelection {
|
||||
if tableView, ok := view.(TableView); ok {
|
||||
return tableView.getCurrent()
|
||||
}
|
||||
}
|
||||
}
|
||||
return CellIndex{Row: -1, Column: -1}
|
||||
}
|
||||
|
||||
// GetTableCellClickedListeners returns listeners of event which occurs when the user clicks on a table cell.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCellClickedListeners(view View, subviewID string) []func(TableView, int, int) {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(TableCellClickedEvent); value != nil {
|
||||
if result, ok := value.([]func(TableView, int, int)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(TableView, int, int){}
|
||||
}
|
||||
|
||||
// GetTableCellSelectedListeners returns listeners of event which occurs when a table cell becomes selected.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableCellSelectedListeners(view View, subviewID string) []func(TableView, int, int) {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(TableCellSelectedEvent); value != nil {
|
||||
if result, ok := value.([]func(TableView, int, int)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(TableView, int, int){}
|
||||
}
|
||||
|
||||
// GetTableRowClickedListeners returns listeners of event which occurs when the user clicks on a table row.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableRowClickedListeners(view View, subviewID string) []func(TableView, int) {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(TableRowClickedEvent); value != nil {
|
||||
if result, ok := value.([]func(TableView, int)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(TableView, int){}
|
||||
}
|
||||
|
||||
// GetTableRowSelectedListeners returns listeners of event which occurs when a table row becomes selected.
|
||||
// If there are no listeners then the empty list is returned.
|
||||
// If the second argument (subviewID) is "" then a value from the first argument (view) is returned.
|
||||
func GetTableRowSelectedListeners(view View, subviewID string) []func(TableView, int) {
|
||||
if subviewID != "" {
|
||||
view = ViewByID(view, subviewID)
|
||||
}
|
||||
if view != nil {
|
||||
if value := view.Get(TableRowSelectedEvent); value != nil {
|
||||
if result, ok := value.([]func(TableView, int)); ok {
|
||||
return result
|
||||
}
|
||||
}
|
||||
}
|
||||
return []func(TableView, int){}
|
||||
}
|
|
@ -44,6 +44,10 @@ func (picker *timePickerData) Init(session Session) {
|
|||
picker.timeChangedListeners = []func(TimePicker, time.Time){}
|
||||
}
|
||||
|
||||
func (picker *timePickerData) Focusable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (picker *timePickerData) normalizeTag(tag string) string {
|
||||
tag = strings.ToLower(tag)
|
||||
switch tag {
|
||||
|
|
18
view.go
18
view.go
|
@ -58,6 +58,8 @@ type View interface {
|
|||
SetAnimated(tag string, value interface{}, animation Animation) bool
|
||||
// SetChangeListener set the function to track the change of the View property
|
||||
SetChangeListener(tag string, listener func(View, string))
|
||||
// HasFocus returns 'true' if the view has focus
|
||||
HasFocus() bool
|
||||
|
||||
handleCommand(self View, command string, data DataObject) bool
|
||||
htmlClass(disabled bool) string
|
||||
|
@ -73,7 +75,7 @@ type View interface {
|
|||
getTransitions() Params
|
||||
|
||||
onResize(self View, x, y, width, height float64)
|
||||
onItemResize(self View, index int, x, y, width, height float64)
|
||||
onItemResize(self View, index string, x, y, width, height float64)
|
||||
setNoResizeEvent()
|
||||
isNoResizeEvent() bool
|
||||
setScroll(x, y, width, height float64)
|
||||
|
@ -95,6 +97,7 @@ type viewData struct {
|
|||
scroll Frame
|
||||
noResizeEvent bool
|
||||
created bool
|
||||
hasFocus bool
|
||||
//animation map[string]AnimationEndListener
|
||||
}
|
||||
|
||||
|
@ -737,7 +740,14 @@ func (view *viewData) handleCommand(self View, command string, data DataObject)
|
|||
case TouchStart, TouchEnd, TouchMove, TouchCancel:
|
||||
handleTouchEvents(self, command, data)
|
||||
|
||||
case FocusEvent, LostFocusEvent:
|
||||
case FocusEvent:
|
||||
view.hasFocus = true
|
||||
for _, listener := range getFocusListeners(view, "", command) {
|
||||
listener(self)
|
||||
}
|
||||
|
||||
case LostFocusEvent:
|
||||
view.hasFocus = false
|
||||
for _, listener := range getFocusListeners(view, "", command) {
|
||||
listener(self)
|
||||
}
|
||||
|
@ -838,3 +848,7 @@ func (view *viewData) SetChangeListener(tag string, listener func(View, string))
|
|||
view.changeListener[tag] = listener
|
||||
}
|
||||
}
|
||||
|
||||
func (view *viewData) HasFocus() bool {
|
||||
return view.hasFocus
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ type WebBrige interface {
|
|||
RunGetterScript(script string) DataObject
|
||||
AnswerReceived(answer DataObject)
|
||||
Close()
|
||||
remoteAddr() string
|
||||
}
|
||||
|
||||
type wsBrige struct {
|
||||
|
@ -122,3 +123,7 @@ func (brige *wsBrige) AnswerReceived(answer DataObject) {
|
|||
ErrorLog("answerID not found")
|
||||
}
|
||||
}
|
||||
|
||||
func (brige *wsBrige) remoteAddr() string {
|
||||
return brige.conn.RemoteAddr().String()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue