mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
ui: create simpler alternative for BeanConfigurable
Drop support for reflective field access, improve type safety. Prohibit overriding 'onApply', 'createComponent' and others. Implement UiDslConfigurable to fix inconsistent gaps between components. GitOrigin-RevId: 0efd98e0d3fc5fa6bf61a5119b2374ea46c5e957
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2e475a418e
commit
ff0b5b7f6b
@@ -10,8 +10,8 @@ import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
|
||||
import com.intellij.openapi.extensions.BaseExtensionPointName;
|
||||
import com.intellij.openapi.options.BeanConfigurable;
|
||||
import com.intellij.openapi.options.CompositeConfigurable;
|
||||
import com.intellij.openapi.options.ConfigurableBuilder;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.ex.ConfigurableWrapper;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -112,12 +112,8 @@ public class CodeFoldingConfigurable extends CompositeConfigurable<CodeFoldingOp
|
||||
|
||||
@NotNull
|
||||
private static String sortByTitle(@NotNull CodeFoldingOptionsProvider p) {
|
||||
if (p instanceof BeanConfigurable) {
|
||||
String title = ((BeanConfigurable)p).getTitle();
|
||||
if (title != null) {
|
||||
return ApplicationBundle.message("title.general").equals(title) ? "" : title;
|
||||
}
|
||||
}
|
||||
return "z";
|
||||
String title = ConfigurableBuilder.getConfigurableTitle(p);
|
||||
if (ApplicationBundle.message("title.general").equals(title)) return "";
|
||||
return title != null ? title : "z";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ package com.intellij.application.options.editor;
|
||||
import com.intellij.ide.ui.OptionsSearchTopHitProvider;
|
||||
import com.intellij.ide.ui.search.OptionDescription;
|
||||
import com.intellij.openapi.application.ApplicationBundle;
|
||||
import com.intellij.openapi.options.BeanConfigurable;
|
||||
import com.intellij.openapi.options.ConfigurableBuilder;
|
||||
import com.intellij.openapi.options.ConfigurableWithOptionDescriptors;
|
||||
import com.intellij.openapi.options.UnnamedConfigurable;
|
||||
import com.intellij.openapi.options.ex.ConfigurableWrapper;
|
||||
@@ -35,7 +35,7 @@ final class CodeFoldingOptionsTopHitProvider implements OptionsSearchTopHitProvi
|
||||
return;
|
||||
}
|
||||
|
||||
String title = configurable instanceof BeanConfigurable ? ((BeanConfigurable<?>)configurable).getTitle() : null;
|
||||
String title = ConfigurableBuilder.getConfigurableTitle(configurable);
|
||||
String prefix = title == null ? byDefault + " " : StringUtil.trimEnd(byDefault, ':') + " in " + title + ": ";
|
||||
result.addAll(((ConfigurableWithOptionDescriptors)configurable).getOptionDescriptors(CodeFoldingConfigurable.ID, s -> prefix + s));
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
* See {@link ConfigurableBuilder} for {@link UiDslConfigurable} alternative.
|
||||
*/
|
||||
public abstract class BeanConfigurable<T> implements UnnamedConfigurable, ConfigurableWithOptionDescriptors {
|
||||
private final T myInstance;
|
||||
@@ -293,7 +293,7 @@ public abstract class BeanConfigurable<T> implements UnnamedConfigurable, Config
|
||||
@Override
|
||||
public List<OptionDescription> getOptionDescriptors(@NotNull String configurableId,
|
||||
@NotNull Function<? super String, String> nameConverter) {
|
||||
List<BeanConfigurable.CheckboxField> boxes = JBIterable.from(myFields).filter(CheckboxField.class).toList();
|
||||
List<CheckboxField> boxes = JBIterable.from(myFields).filter(CheckboxField.class).toList();
|
||||
Object instance = getInstance();
|
||||
return ContainerUtil.map(boxes, box -> new BooleanOptionDescription(nameConverter.apply(box.getTitle()), configurableId) {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.options;
|
||||
|
||||
import com.intellij.ide.ui.search.BooleanOptionDescription;
|
||||
import com.intellij.ide.ui.search.OptionDescription;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Getter;
|
||||
import com.intellij.openapi.util.Setter;
|
||||
import com.intellij.ui.layout.RowBuilder;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.JBIterable;
|
||||
import kotlin.reflect.KMutableProperty0;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* See also {@link UiDslConfigurable.Simple} for more flexible alternative.
|
||||
*/
|
||||
public abstract class ConfigurableBuilder extends UiDslConfigurable.Simple
|
||||
implements UiDslConfigurable, ConfigurableWithOptionDescriptors {
|
||||
private String myTitle;
|
||||
|
||||
private interface PropertyAccessor<T> {
|
||||
T getValue();
|
||||
|
||||
void setValue(@NotNull T value);
|
||||
}
|
||||
|
||||
private static class CallbackAccessor<T> implements PropertyAccessor<T> {
|
||||
private final Getter<? extends T> myGetter;
|
||||
private final Setter<? super T> mySetter;
|
||||
|
||||
private CallbackAccessor(Getter<? extends T> getter, Setter<? super T> setter) {
|
||||
myGetter = getter;
|
||||
mySetter = setter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return myGetter.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(@NotNull T value) {
|
||||
mySetter.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
private static class KPropertyAccessor<T> implements PropertyAccessor<T> {
|
||||
private final KMutableProperty0<T> myProperty;
|
||||
|
||||
private KPropertyAccessor(KMutableProperty0<T> property) {
|
||||
myProperty = property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue() {
|
||||
return myProperty.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(@NotNull T value) {
|
||||
myProperty.set(value);
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class BeanField<C extends JComponent, T> {
|
||||
protected final PropertyAccessor<T> myAccessor;
|
||||
private C myComponent;
|
||||
|
||||
private BeanField(@NotNull PropertyAccessor<T> accessor) {
|
||||
myAccessor = accessor;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
C getComponent() {
|
||||
if (myComponent == null) {
|
||||
myComponent = createComponent();
|
||||
}
|
||||
return myComponent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected abstract C createComponent();
|
||||
|
||||
boolean isModified() {
|
||||
final Object componentValue = getComponentValue();
|
||||
final Object beanValue = myAccessor.getValue();
|
||||
return !Comparing.equal(componentValue, beanValue);
|
||||
}
|
||||
|
||||
void apply() {
|
||||
myAccessor.setValue(getComponentValue());
|
||||
}
|
||||
|
||||
void reset() {
|
||||
setComponentValue(myAccessor.getValue());
|
||||
}
|
||||
|
||||
protected abstract T getComponentValue();
|
||||
|
||||
protected abstract void setComponentValue(T value);
|
||||
}
|
||||
|
||||
private static class CheckboxField extends BeanField<JCheckBox, @NotNull Boolean> {
|
||||
private final String myTitle;
|
||||
|
||||
private CheckboxField(PropertyAccessor<Boolean> accessor, @NotNull String title) {
|
||||
super(accessor);
|
||||
myTitle = title;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getTitle() {
|
||||
return myTitle;
|
||||
}
|
||||
|
||||
private void setAccessorValue(boolean value) {
|
||||
myAccessor.setValue(value);
|
||||
}
|
||||
|
||||
private boolean getAccessorValue() {
|
||||
return myAccessor.getValue();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected JCheckBox createComponent() {
|
||||
return new JCheckBox(myTitle);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean getComponentValue() {
|
||||
return getComponent().isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setComponentValue(@NotNull Boolean value) {
|
||||
getComponent().setSelected(value.booleanValue());
|
||||
}
|
||||
}
|
||||
|
||||
private final List<BeanField<?, ?>> myFields = new ArrayList<>();
|
||||
|
||||
protected ConfigurableBuilder() {
|
||||
}
|
||||
|
||||
protected ConfigurableBuilder(@Nullable String title) {
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return myTitle;
|
||||
}
|
||||
|
||||
protected void setTitle(@Nullable String title) {
|
||||
myTitle = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds check box with given {@code title}.
|
||||
* Initial checkbox value is obtained from {@code getter}.
|
||||
* After the apply, the value from the check box is written back to model via {@code setter}.
|
||||
*/
|
||||
protected void checkBox(@NotNull String title, @NotNull Getter<@NotNull Boolean> getter, @NotNull Setter<? super Boolean> setter) {
|
||||
myFields.add(new CheckboxField(new CallbackAccessor<>(getter, setter), title));
|
||||
}
|
||||
|
||||
protected void checkBox(@NotNull String title, @NotNull KMutableProperty0<@NotNull Boolean> prop) {
|
||||
myFields.add(new CheckboxField(new KPropertyAccessor<>(prop), title));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds custom component (e.g. edit box).
|
||||
* Initial value is obtained from {@code beanGetter} and applied to the component via {@code componentSetter}.
|
||||
* E.g. text is read from the model and set to the edit box.
|
||||
* After the apply, the value from the component is queried via {@code componentGetter} and written back to model via {@code beanSetter}.
|
||||
* E.g. text from the edit box is queried and saved back to model bean.
|
||||
*/
|
||||
protected <V> void component(@NotNull JComponent component,
|
||||
@NotNull Getter<? extends V> beanGetter,
|
||||
@NotNull Setter<? super V> beanSetter,
|
||||
@NotNull Getter<? extends V> componentGetter,
|
||||
@NotNull Setter<? super V> componentSetter) {
|
||||
BeanField<JComponent, V> field = new BeanField<JComponent, V>(new CallbackAccessor<>(beanGetter, beanSetter)) {
|
||||
@NotNull
|
||||
@Override
|
||||
protected JComponent createComponent() {
|
||||
return component;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected V getComponentValue() {
|
||||
return componentGetter.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setComponentValue(V value) {
|
||||
componentSetter.set(value);
|
||||
}
|
||||
};
|
||||
myFields.add(field);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<OptionDescription> getOptionDescriptors(@NotNull String configurableId,
|
||||
@NotNull Function<? super String, String> nameConverter) {
|
||||
List<ConfigurableBuilder.CheckboxField> boxes = JBIterable.from(myFields).filter(CheckboxField.class).toList();
|
||||
return ContainerUtil.map(boxes, box -> new BooleanOptionDescription(nameConverter.apply(box.getTitle()), configurableId) {
|
||||
@Override
|
||||
public boolean isOptionEnabled() {
|
||||
return box.getAccessorValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptionState(boolean enabled) {
|
||||
box.setAccessorValue(enabled);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createComponentRow(@NotNull RowBuilder builder) {
|
||||
ConfigurableBuilderHelper.buildFieldsPanel$intellij_platform_ide_impl(builder, myTitle, myFields);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getConfigurableTitle(@NotNull UnnamedConfigurable configurable) {
|
||||
if (configurable instanceof BeanConfigurable<?>) {
|
||||
return ((BeanConfigurable<?>)configurable).getTitle();
|
||||
}
|
||||
if (configurable instanceof ConfigurableBuilder) {
|
||||
return ((ConfigurableBuilder)configurable).getTitle();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.openapi.options
|
||||
|
||||
import com.intellij.ui.layout.*
|
||||
|
||||
class ConfigurableBuilderHelper {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
internal fun RowBuilder.buildFieldsPanel(title: String?, fields: List<ConfigurableBuilder.BeanField<*, *>>) {
|
||||
if (title != null) {
|
||||
titledRow(title) {
|
||||
appendFields(fields)
|
||||
}
|
||||
}
|
||||
else {
|
||||
appendFields(fields)
|
||||
}
|
||||
}
|
||||
|
||||
private fun RowBuilder.appendFields(fields: List<ConfigurableBuilder.BeanField<*, *>>) {
|
||||
for (field in fields) {
|
||||
row {
|
||||
component(field.component)
|
||||
.onApply { field.apply() }
|
||||
.onIsModified { field.isModified() }
|
||||
.onReset { field.reset() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user