Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add categories and dividers for dropdowns #2398

Merged
merged 1 commit into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions backend/src/nodes/impl/resize.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ class ResizeFilter(Enum):
CATROM = 3
LANCZOS = 1

# HERMITE = 5
# MITCHELL = 6
# BSPLINE = 7
# HAMMING = 8
# HANN = 9
# LAGRANGE = 10
# GAUSS = 11
HERMITE = 5
MITCHELL = 6
BSPLINE = 7
HAMMING = 8
HANN = 9
LAGRANGE = 10
GAUSS = 11


_FILTER_MAP: dict[ResizeFilter, NavtiveResizeFilter] = {
Expand All @@ -32,13 +32,13 @@ class ResizeFilter(Enum):
ResizeFilter.LINEAR: NavtiveResizeFilter.Linear,
ResizeFilter.CATROM: NavtiveResizeFilter.CubicCatrom,
ResizeFilter.LANCZOS: NavtiveResizeFilter.Lanczos,
# ResizeFilter.HERMITE: NavtiveResizeFilter.Hermite,
# ResizeFilter.MITCHELL: NavtiveResizeFilter.CubicMitchell,
# ResizeFilter.BSPLINE: NavtiveResizeFilter.CubicBSpline,
# ResizeFilter.HAMMING: NavtiveResizeFilter.Hamming,
# ResizeFilter.HANN: NavtiveResizeFilter.Hann,
# ResizeFilter.LAGRANGE: NavtiveResizeFilter.Lagrange,
# ResizeFilter.GAUSS: NavtiveResizeFilter.Gauss,
ResizeFilter.HERMITE: NavtiveResizeFilter.Hermite,
ResizeFilter.MITCHELL: NavtiveResizeFilter.CubicMitchell,
ResizeFilter.BSPLINE: NavtiveResizeFilter.CubicBSpline,
ResizeFilter.HAMMING: NavtiveResizeFilter.Hamming,
ResizeFilter.HANN: NavtiveResizeFilter.Hann,
ResizeFilter.LAGRANGE: NavtiveResizeFilter.Lagrange,
ResizeFilter.GAUSS: NavtiveResizeFilter.Gauss,
}


Expand Down
39 changes: 36 additions & 3 deletions backend/src/nodes/properties/inputs/generic_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import re
from dataclasses import dataclass
from enum import Enum
from typing import Any, Generic, Literal, TypedDict, TypeVar, Union

Expand Down Expand Up @@ -51,6 +52,22 @@ class TypedOption(TypedDict):
"""


@dataclass
class DropDownGroup:
label: str | None
start_at: str | int | Enum

@staticmethod
def divider(start_at: str | int | Enum):
return DropDownGroup(None, start_at)

def to_dict(self):
start_at = self.start_at
if isinstance(start_at, Enum):
start_at = start_at.value
return {"label": self.label, "startAt": start_at}


class DropDownInput(BaseInput):
"""Input for a dropdown"""

Expand All @@ -61,6 +78,7 @@ def __init__(
options: list[DropDownOption],
default_value: str | int | None = None,
preferred_style: DropDownStyle = "dropdown",
groups: list[DropDownGroup] | None = None,
associated_type: Any = None,
):
super().__init__(input_type, label, kind="dropdown", has_handle=False)
Expand All @@ -70,6 +88,7 @@ def __init__(
default_value if default_value is not None else options[0]["value"]
)
self.preferred_style: DropDownStyle = preferred_style
self.groups: list[DropDownGroup] = groups or []

if self.default not in self.accepted_values:
logger.error(
Expand All @@ -87,6 +106,7 @@ def to_dict(self):
"options": self.options,
"def": self.default,
"preferredStyle": self.preferred_style,
"groups": [c.to_dict() for c in self.groups],
}

def make_optional(self):
Expand Down Expand Up @@ -157,6 +177,7 @@ def __init__(
option_labels: dict[T, str] | None = None,
extra_definitions: str | None = None,
preferred_style: DropDownStyle = "dropdown",
categories: list[DropDownGroup] | None = None,
):
if type_name is None:
type_name = enum.__name__
Expand Down Expand Up @@ -189,6 +210,7 @@ def __init__(
options=options,
default_value=default.value if default is not None else None,
preferred_style=preferred_style,
groups=categories,
)

self.type_definitions = (
Expand Down Expand Up @@ -558,6 +580,12 @@ def BlendModeDropdown() -> DropDownInput:
option_labels={
BlendMode.ADD: "Linear Dodge (Add)",
},
categories=[
DropDownGroup.divider(start_at=BlendMode.DARKEN),
DropDownGroup.divider(start_at=BlendMode.LIGHTEN),
DropDownGroup.divider(start_at=BlendMode.OVERLAY),
DropDownGroup.divider(start_at=BlendMode.DIFFERENCE),
],
)


Expand Down Expand Up @@ -609,9 +637,9 @@ def TileSizeDropdown(
("BC5_SNORM", "BC5 (8bpp, Signed, 2-channel normal)"),
("BC7_UNORM_SRGB", "BC7 (8bpp, sRGB, 8-bit Alpha)"),
("BC7_UNORM", "BC7 (8bpp, Linear, 8-bit Alpha)"),
("DXT1", "DXT1 (4bpp, Linear, 1-bit Alpha, Legacy)"),
("DXT3", "DXT3 (8bpp, Linear, 4-bit Alpha, Legacy)"),
("DXT5", "DXT5 (8bpp, Linear, 8-bit Alpha, Legacy)"),
("DXT1", "DXT1 (4bpp, Linear, 1-bit Alpha)"),
("DXT3", "DXT3 (8bpp, Linear, 4-bit Alpha)"),
("DXT5", "DXT5 (8bpp, Linear, 8-bit Alpha)"),
("R8G8B8A8_UNORM_SRGB", "RGBA (32bpp, sRGB, 8-bit Alpha)"),
("R8G8B8A8_UNORM", "RGBA (32bpp, Linear, 8-bit Alpha)"),
("B8G8R8A8_UNORM_SRGB", "BGRA (32bpp, sRGB, 8-bit Alpha)"),
Expand All @@ -631,6 +659,11 @@ def DdsFormatDropdown() -> DropDownInput:
label="DDS Format",
options=[{"option": title, "value": f} for f, title in SUPPORTED_DDS_FORMATS],
associated_type=DDSFormat,
groups=[
DropDownGroup("Compressed", start_at="BC1_UNORM_SRGB"),
DropDownGroup("Uncompressed", start_at="R8G8B8A8_UNORM_SRGB"),
DropDownGroup("Legacy Compressed", start_at="DXT1"),
],
)


Expand Down
7 changes: 6 additions & 1 deletion backend/src/nodes/properties/inputs/image_dropdown_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from ...impl.image_utils import BorderType
from ...impl.pil_utils import RotationInterpolationMethod
from ...impl.resize import ResizeFilter
from .generic_inputs import DropDownInput, EnumInput
from .generic_inputs import DropDownGroup, DropDownInput, EnumInput


def ColorSpaceDetectorInput(label: str = "Color Space") -> DropDownInput:
Expand Down Expand Up @@ -55,10 +55,15 @@ def ResizeFilterInput() -> DropDownInput:
return EnumInput(
ResizeFilter,
label="Interpolation Method",
categories=[
DropDownGroup("Basic", start_at=ResizeFilter.AUTO),
DropDownGroup("Advanced", start_at=ResizeFilter.HERMITE),
],
option_labels={
ResizeFilter.NEAREST: "Nearest Neighbor",
ResizeFilter.BOX: "Area (Box)",
ResizeFilter.CATROM: "Cubic",
ResizeFilter.BSPLINE: "B-Spline",
},
)

Expand Down
5 changes: 5 additions & 0 deletions src/common/common-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ export interface InputOption {
}
export type FileInputKind = 'image' | 'pth' | 'pt' | 'video' | 'bin' | 'param' | 'onnx';
export type DropDownStyle = 'dropdown' | 'checkbox' | 'tabs';
export interface DropdownGroup {
readonly label?: string | null;
readonly startAt: InputSchemaValue;
}

export interface GenericInput extends InputBase {
readonly kind: 'generic';
Expand All @@ -76,6 +80,7 @@ export interface DropDownInput extends InputBase {
readonly def: string | number;
readonly options: readonly InputOption[];
readonly preferredStyle: DropDownStyle;
readonly groups: readonly DropdownGroup[];
}
export interface FileInput extends InputBase {
readonly kind: 'file';
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/components/inputs/DropDownInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { InputProps } from './props';
type DropDownInputProps = InputProps<'dropdown', string | number>;

export const DropDownInput = memo(({ value, setValue, input, isLocked }: DropDownInputProps) => {
const { options, def, label, preferredStyle } = input;
const { options, def, label, preferredStyle, groups } = input;

const reset = useCallback(() => setValue(def), [setValue, def]);

Expand Down Expand Up @@ -46,6 +46,7 @@ export const DropDownInput = memo(({ value, setValue, input, isLocked }: DropDow
return (
<WithLabel input={input}>
<DropDown
groups={groups}
isDisabled={isLocked}
options={input.options}
reset={reset}
Expand Down
133 changes: 93 additions & 40 deletions src/renderer/components/inputs/elements/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,56 +1,109 @@
import { Select } from '@chakra-ui/react';
import { ChangeEvent, memo, useEffect } from 'react';
import { DropDownInput, InputSchemaValue } from '../../../../common/common-types';
import { DropDownInput, DropdownGroup, InputSchemaValue } from '../../../../common/common-types';

export interface DropDownProps {
value: InputSchemaValue | undefined;
onChange: (value: InputSchemaValue) => void;
reset: () => void;
isDisabled?: boolean;
options: DropDownInput['options'];
groups?: readonly DropdownGroup[];
}

export const DropDown = memo(({ value, onChange, reset, isDisabled, options }: DropDownProps) => {
// reset invalid values to default
useEffect(() => {
if (value === undefined || options.every((o) => o.value !== value)) {
reset();
}
}, [value, reset, options]);

let selection = options.findIndex((o) => o.value === value);
if (selection === -1) selection = 0;

const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedIndex = Number(event.target.value);
const selectedValue = options[selectedIndex]?.value as InputSchemaValue | undefined;
if (selectedValue === undefined) {
reset();
} else {
onChange(selectedValue);
}
};

return (
<Select
borderRadius="lg"
className="nodrag"
disabled={isDisabled}
draggable={false}
size="sm"
style={{ contain: 'size' }}
value={selection}
onChange={handleChange}
>
{options.map(({ option }, index) => (
export const DropDown = memo(
({ value, onChange, reset, isDisabled, options, groups }: DropDownProps) => {
// reset invalid values to default
useEffect(() => {
if (value === undefined || options.every((o) => o.value !== value)) {
reset();
}
}, [value, reset, options]);

let selection = options.findIndex((o) => o.value === value);
if (selection === -1) selection = 0;

const handleChange = (event: ChangeEvent<HTMLSelectElement>) => {
const selectedIndex = Number(event.target.value);
const selectedValue = options[selectedIndex]?.value as InputSchemaValue | undefined;
if (selectedValue === undefined) {
reset();
} else {
onChange(selectedValue);
}
};

const optionElements: JSX.Element[] = [];
// eslint-disable-next-line @typescript-eslint/no-shadow
for (const [o, index] of options.map((o, i) => [o, i] as const)) {
const group = groups?.find((c) => c.startAt === o.value);
if (group) {
const pseudoValue = `--category-${index}-${group.label ?? ''}`;

if (group.label) {
optionElements.push(
<option
disabled
key={optionElements.length}
style={{ fontSize: '30%' }}
value={`--pad-${pseudoValue}`}
>
{' '}
</option>,
<option
disabled
key={optionElements.length + 1}
style={{
fontSize: '110%',
fontWeight: 'bold',
textAlign: 'center',
}}
value={pseudoValue}
>
{group.label}
</option>
);
} else {
optionElements.push(
<option
disabled
key={optionElements.length}
style={{
fontSize: '85%',
color: 'rgba(128 128 128 / 70%)',
}}
value={`--hr-${pseudoValue}`}
>
{'\u23AF'.repeat(30)}
</option>
);
}
}

optionElements.push(
<option
key={option}
key={optionElements.length}
style={{ fontSize: '120%' }}
value={index}
>
{option}
{o.option}
</option>
))}
</Select>
);
});
);
}

return (
<Select
borderRadius="lg"
className="nodrag"
disabled={isDisabled}
draggable={false}
size="sm"
style={{ contain: 'size' }}
value={selection}
onChange={handleChange}
>
{optionElements}
</Select>
);
}
);