Skip to content
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.spinyowl.spinygui.core.node;

import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_DISABLED;
import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_TYPE;
import static com.spinyowl.spinygui.core.node.NodeBuilder.NODE_BUTTON;
import static com.spinyowl.spinygui.core.node.NodeBuilder.TYPE_BUTTON;
Expand Down Expand Up @@ -37,6 +38,11 @@ public void type(String type) {
this.type = type == null || type.isBlank() ? TYPE_SUBMIT : type;
}

@Override
public boolean disabled() {
return hasAttribute(ATTR_DISABLED);
}

public boolean plainButton() {
return TYPE_BUTTON.equalsIgnoreCase(type);
}
Expand All @@ -50,6 +56,6 @@ public boolean resetButton() {
}

public boolean activatable() {
return plainButton() || submitButton() || resetButton();
return !disabled() && (plainButton() || submitButton() || resetButton());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ public boolean hasAttribute(String attribute) {
return attributes.containsKey(attribute);
}

/** Returns whether this element is a control disabled by its {@code disabled} attribute. */
public boolean disabled() {
return false;
}

@Override
public void removeAttribute(String attribute) {
attributes.remove(attribute);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.spinyowl.spinygui.core.node;

import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_DISABLED;
import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_TYPE;
import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_VALUE;
import static com.spinyowl.spinygui.core.node.NodeBuilder.NODE_INPUT;
Expand Down Expand Up @@ -115,6 +116,11 @@ public void textScrollLeft(float textScrollLeft) {
this.textScrollLeft = Math.max(0, textScrollLeft);
}

@Override
public boolean disabled() {
return hasAttribute(ATTR_DISABLED);
}

public boolean textInput() {
return TYPE_TEXT.equalsIgnoreCase(type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public final class NodeBuilder {
public static final String ATTR_TYPE = "type";
public static final String ATTR_NAME = "name";
public static final String ATTR_VALUE = "value";
public static final String ATTR_DISABLED = "disabled";
public static final String ATTR_ROWS = "rows";
public static final String ATTR_COLS = "cols";

Expand Down Expand Up @@ -99,6 +100,11 @@ public static Attribute value(String value) {
return attr(ATTR_VALUE, value);
}

/** Creates a boolean {@code disabled} attribute. */
public static Attribute disabled() {
return attr(ATTR_DISABLED, "");
}

public static Attribute rows(String value) {
return attr(ATTR_ROWS, value);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.spinyowl.spinygui.core.node;

import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_DISABLED;
import static com.spinyowl.spinygui.core.node.NodeBuilder.NODE_TEXTAREA;

import java.util.Map;
Expand Down Expand Up @@ -105,6 +106,11 @@ public void textScrollLeft(float textScrollLeft) {
this.textScrollLeft = Math.max(0, textScrollLeft);
}

@Override
public boolean disabled() {
return hasAttribute(ATTR_DISABLED);
}

private int clampTextIndex(int index) {
return TextIndexNormalizer.clampAndSnapBackward(value, index);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.spinyowl.spinygui.core.style.stylesheet.selector.combinator.DescendantSelector;
import com.spinyowl.spinygui.core.style.stylesheet.selector.combinator.GeneralSiblingSelector;
import com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass.ActiveSelector;
import com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass.DisabledSelector;
import com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass.FocusSelector;
import com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass.HoverSelector;
import com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoelement.AfterSelector;
Expand Down Expand Up @@ -99,6 +100,10 @@ public Selector visitPseudo(PseudoContext ctx) {
return new ActiveSelector();
}

if ("disabled".equals(selectorName)) {
return new DisabledSelector();
}

if ("before".equals(selectorName)) {
return new BeforeSelector();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass;

import com.spinyowl.spinygui.core.node.Element;
import com.spinyowl.spinygui.core.style.stylesheet.selector.PseudoClassSelector;

/** Matches supported form controls that currently have the boolean {@code disabled} attribute. */
public class DisabledSelector implements PseudoClassSelector {

@Override
public boolean test(Element element) {
return element.disabled();
}

@Override
public String toString() {
return ":disabled";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void processWithImpact(
private void processInternal(
SystemCharEvent event, Frame frame, InputProcessingBatch batch) {
var focusedElement = frame.getFocusedElement();
if (focusedElement == null) {
if (focusedElement == null || focusedElement.disabled()) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ public void processWithImpact(
private void processInternal(
SystemKeyEvent event, Frame frame, InputProcessingBatch batch) {
var element = frame.getFocusedElement();
if (element != null && element.disabled()) {
element.pressed(false);
return;
}
if (element == null && batch == null) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,16 @@ public void process(@NonNull SystemMouseClickEvent event, @NonNull Frame frame)
processWithNoTarget(event, frame, focusedElement, currentCursorPosition);
} else {
target = buttonOwner(target);
if (target.disabled()) {
target.pressed(false);
if (event.action() == RELEASE && focusedElement != null && focusedElement != target) {
focusedElement.pressed(false);
if (!focusedElement.disabled()) {
generateReleaseEvent(event, frame, focusedElement, currentCursorPosition);
}
}
return;
}
processWithExistingTarget(event, frame, focusedElement, currentCursorPosition, target);
}
}
Expand Down Expand Up @@ -291,7 +301,7 @@ private void generateActionEvent(Frame frame, Element target) {

private boolean activatable(Element target) {
return target instanceof ButtonElement button && button.activatable()
|| target instanceof InputElement input && input.buttonInput();
|| target instanceof InputElement input && input.buttonInput() && !input.disabled();
}

private Element buttonOwner(Element target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,16 @@ public boolean handleKey(ButtonElement button, KeyCode keyCode, KeyAction action
}

public boolean handleKey(InputElement input, KeyCode keyCode, KeyAction action) {
return handleKey(input, input.buttonInput(), keyCode, action);
return handleKey(input, input.buttonInput() && !input.disabled(), keyCode, action);
}

private boolean handleKey(
Element element, boolean activatable, KeyCode keyCode, KeyAction action) {
if (!activatable || !activationKey(keyCode)) {
if (!activatable) {
element.pressed(false);
return false;
}
if (!activationKey(keyCode)) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.spinyowl.spinygui.core.node;

import static com.spinyowl.spinygui.core.node.NodeBuilder.ATTR_DISABLED;
import static com.spinyowl.spinygui.core.node.NodeBuilder.TYPE_BUTTON;
import static com.spinyowl.spinygui.core.node.NodeBuilder.attrs;
import static com.spinyowl.spinygui.core.node.NodeBuilder.button;
import static com.spinyowl.spinygui.core.node.NodeBuilder.disabled;
import static com.spinyowl.spinygui.core.node.NodeBuilder.div;
import static com.spinyowl.spinygui.core.node.NodeBuilder.input;
import static com.spinyowl.spinygui.core.node.NodeBuilder.textarea;
import static com.spinyowl.spinygui.core.node.NodeBuilder.type;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class DisabledAttributeTest {

@Test
void disabledAttributeMarksSupportedControlsDisabled() {
ButtonElement button = button(attrs(disabled()));
InputElement input = input(attrs(disabled()));
TextareaElement textarea = textarea(attrs(disabled()), "value");

assertTrue(button.disabled());
assertTrue(input.disabled());
assertTrue(textarea.disabled());
}

@Test
void disabledAttributeUsesBooleanPresenceSemantics() {
InputElement input = input();

input.setAttribute(ATTR_DISABLED, "false");
assertTrue(input.disabled());

input.removeAttribute(ATTR_DISABLED);
assertFalse(input.disabled());
}

@Test
void unsupportedElementDoesNotBecomeDisabled() {
Element element = div(attrs(disabled()));

assertFalse(element.disabled());
}

@Test
void disabledButtonIsNotActivatable() {
ButtonElement button = button(attrs(disabled(), type(TYPE_BUTTON)));

assertFalse(button.activatable());

button.removeAttribute(ATTR_DISABLED);
assertTrue(button.activatable());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.spinyowl.spinygui.core.style.stylesheet.selector.pseudoclass;

import static com.spinyowl.spinygui.core.node.NodeBuilder.attrs;
import static com.spinyowl.spinygui.core.node.NodeBuilder.button;
import static com.spinyowl.spinygui.core.node.NodeBuilder.disabled;
import static com.spinyowl.spinygui.core.node.NodeBuilder.div;
import static com.spinyowl.spinygui.core.node.NodeBuilder.input;
import static com.spinyowl.spinygui.core.node.NodeBuilder.textarea;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

class DisabledSelectorTest {

private final DisabledSelector selector = new DisabledSelector();

@Test
void matchesDisabledSupportedControls() {
assertTrue(selector.test(button(attrs(disabled()))));
assertTrue(selector.test(input(attrs(disabled()))));
assertTrue(selector.test(textarea(attrs(disabled()), "value")));
}

@Test
void doesNotMatchEnabledOrUnsupportedElements() {
assertFalse(selector.test(button()));
assertFalse(selector.test(input()));
assertFalse(selector.test(textarea()));
assertFalse(selector.test(div(attrs(disabled()))));
}
}
Loading
Loading