Skip to content

Commit

Permalink
Add categories and dividers for dropdowns (#2398)
Browse files Browse the repository at this point in the history
  • Loading branch information
RunDevelopment committed Dec 12, 2023
1 parent db40fdb commit 2521b32
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 59 deletions.
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>
);
}
);

0 comments on commit 2521b32

Please sign in to comment.