private void initializeListenersAndBindings()

in android-npw/src/com/android/tools/idea/npw/assetstudio/ui/ConfigureAdaptiveIconPanel.java [618:860]


  private void initializeListenersAndBindings() {
    myForegroundTrimmed = new SelectedProperty(myForegroundTrimYesRadioButton);
    myBackgroundTrimmed = new SelectedProperty(myBackgroundTrimYesRadioButton);

    myForegroundResizePercent = bindTwoWay(myGeneralBindings, myForegroundResizeSlider, myForegroundResizeValueTextField);
    myBackgroundResizePercent = bindTwoWay(myGeneralBindings, myBackgroundResizeSlider, myBackgroundResizeValueTextField);

    myForegroundColor = new ColorProperty(myForegroundColorPanel);
    myBackgroundColor = ObjectProperty.wrap(new ColorProperty(myBackgroundColorPanel));
    myGenerateLegacyIcon = new SelectedProperty(myGenerateLegacyIconYesRadioButton);
    myGenerateRoundIcon = new SelectedProperty(myGenerateRoundIconYesRadioButton);
    myGeneratePlayStoreIcon = new SelectedProperty(myGeneratePlayStoreIconYesRadioButton);
    myGenerateWebpIcons = new SelectedProperty(myIconFormatWebpRadioButton);

    myLegacyIconShape = ObjectProperty.wrap(new SelectedItemProperty<>(myLegacyIconShapeComboBox));

    updateBindingsAndUiForActiveIconType();

    // Update foreground layer asset type depending on asset type radio buttons.
    myForegroundAssetType.addListener(() -> {
      AssetComponent<?> assetComponent = myForegroundAssetPanelMap.get(myForegroundAssetType.get());
      myForegroundActiveAsset.set(assetComponent.getAsset());
    });

    // Update background asset depending on asset type radio buttons.
    myBackgroundAssetType.addListener(() -> {
      if (myBackgroundAssetType.get() == BackgroundAssetType.IMAGE) {
        myBackgroundImageAsset.setValue(myBackgroundImageAssetBrowser.getAsset());
      }
      else {
        myBackgroundImageAsset.clear();
      }
    });

    // If any of our underlying asset panels change, we should pass that on to anyone listening to
    // us as well.
    ActionListener assetPanelListener = e -> fireAssetListeners();
    for (AssetComponent<?> assetComponent : myForegroundAssetPanelMap.values()) {
      assetComponent.addAssetListener(assetPanelListener);
    }

    myBackgroundImageAssetBrowser.addAssetListener(assetPanelListener);

    Runnable onAssetModified = this::fireAssetListeners;

    // Property to check if the asset used for monochrome layer is resizable.
    // This is used to show or hide controls based on the type of the asset.
    // For example: the resize slider control.
    BoolValueProperty monochromeIsResizable = new BoolValueProperty();

    // Bindings for monochrome components only if monochrome is supported.
    if (myIsMonochromeSupported) {
      myMonochromeTrimmed = new SelectedProperty(myMonochromeTrimYesRadioButton);
      myMonochromeResizePercent = bindTwoWay(myGeneralBindings, myMonochromeResizeSlider, myMonochromeResizeValueTextField);
      myMonochromeColor = new ColorProperty(myMonochromeColorPanel);

      // Update monochrome layer asset type depending on asset type radio buttons.
      myMonochromeAssetType.addListener(() -> {
        AssetComponent<?> assetComponent = myMonochromeAssetPanelMap.get(myMonochromeAssetType.get());
        myMonochromeActiveAsset.setNullableValue(assetComponent.getAsset());
      });
      for (AssetComponent<?> assetComponent : myMonochromeAssetPanelMap.values()) {
        assetComponent.addAssetListener(assetPanelListener);
      }
      myListeners
        .listenAll(myForegroundTrimmed, myForegroundResizePercent, myForegroundColor,
                   myBackgroundTrimmed, myBackgroundResizePercent, myBackgroundColor,
                   myMonochromeTrimmed, myMonochromeResizePercent, myMonochromeColor,
                   myGenerateLegacyIcon, myLegacyIconShape,
                   myGenerateRoundIcon, myGeneratePlayStoreIcon, myGenerateWebpIcons)
        .with(onAssetModified);

      // Listens for changes to the controls on the foreground panel. When the asset is changed,
      // it updates the controller bindings to match the properties of the new asset.
      myListeners.listenAndFire(myMonochromeActiveAsset, () -> {
        myMonochromeActiveAssetBindings.releaseAll();
        BaseAsset asset = myMonochromeActiveAsset.getValueOrNull();
        if (asset != null) {
          myMonochromeActiveAssetBindings.bindTwoWay(myMonochromeTrimmed, asset.trimmed());
          myMonochromeActiveAssetBindings.bindTwoWay(myMonochromeResizePercent, asset.scalingPercent());
          OptionalValueProperty<Color> assetColor = asset.color();
          if (assetColor.getValueOrNull() == null) {
            assetColor.setNullableValue(myMonochromeColor.getValueOrNull());
          }
          myMonochromeActiveAssetBindings.bindTwoWay(myMonochromeColor, assetColor);
          myMonochromeActiveAssetBindings.bind(monochromeIsResizable, asset.isResizable());
          if (asset instanceof ImageAsset) {
            myMonochromeActiveAssetBindings.bind(myMonochromeAssetValidityState, ((ImageAsset)asset).getValidityState());
          }
          else {
            myMonochromeAssetValidityState.set(Validator.Result.OK);
          }
        }
        getIconGenerator().monochromeImageAsset().setNullableValue(asset);
        onAssetModified.run();
      });
    }
    else {
      // If monochrome is not supported we add all the non-monochrome observable listeners
      myListeners
        .listenAll(myForegroundTrimmed, myForegroundResizePercent, myForegroundColor,
                   myBackgroundTrimmed, myBackgroundResizePercent, myBackgroundColor,
                   myGenerateLegacyIcon, myLegacyIconShape,
                   myGenerateRoundIcon, myGeneratePlayStoreIcon, myGenerateWebpIcons)
        .with(onAssetModified);
    }

    // Property to check if the asset used for foreground layer is resizable.
    // This is used to show or hide controls based on the type of the asset.
    // For example: the resize slider control.
    BoolValueProperty foregroundIsResizable = new BoolValueProperty();
    // Listens for changes to the controls on the foreground panel. When the asset is changed,
    // it updates the controller bindings to match the properties of the new asset.
    myListeners.listenAndFire(myForegroundActiveAsset, () -> {
      myForegroundActiveAssetBindings.releaseAll();
      BaseAsset asset = myForegroundActiveAsset.get();
      myForegroundActiveAssetBindings.bindTwoWay(myForegroundTrimmed, asset.trimmed());
      myForegroundActiveAssetBindings.bindTwoWay(myForegroundResizePercent, asset.scalingPercent());
      OptionalValueProperty<Color> assetColor = asset.color();
      if (assetColor.getValueOrNull() == null) {
        assetColor.setNullableValue(myForegroundColor.getValueOrNull());
      }
      myForegroundActiveAssetBindings.bindTwoWay(myForegroundColor, assetColor);
      myForegroundActiveAssetBindings.bind(foregroundIsResizable, asset.isResizable());
      if (asset instanceof ImageAsset) {
        myForegroundActiveAssetBindings.bind(myForegroundAssetValidityState, ((ImageAsset)asset).getValidityState());
      }
      else {
        myForegroundAssetValidityState.set(Validator.Result.OK);
      }

      getIconGenerator().sourceAsset().setValue(asset);
      onAssetModified.run();
    });

    BoolValueProperty backgroundIsResizable = new BoolValueProperty();
    // When switching between Image/Color for background, bind corresponding properties and regenerate asset (to be sure).
    Runnable onBackgroundAssetModified = () -> {
      myBackgroundActiveAssetBindings.releaseAll();
      ImageAsset asset = myBackgroundImageAsset.getValueOrNull();
      if (asset != null) {
        myBackgroundActiveAssetBindings.bindTwoWay(myBackgroundTrimmed, asset.trimmed());
        myBackgroundActiveAssetBindings.bindTwoWay(myBackgroundResizePercent, asset.scalingPercent());
        myBackgroundActiveAssetBindings.bind(backgroundIsResizable, asset.isResizable());
        myBackgroundActiveAssetBindings.bind(myBackgroundAssetValidityState, asset.getValidityState());
      }
      else {
        backgroundIsResizable.set(false);
        myBackgroundAssetValidityState.set(Validator.Result.OK);
      }
      getIconGenerator().backgroundImageAsset().setNullableValue(asset);
      onAssetModified.run();
    };
    myListeners.listenAndFire(myBackgroundImageAsset, onBackgroundAssetModified::run);

    /*
     * Hook up a bunch of UI <- boolean expressions, so that when certain conditions are met,
     * various components show/hide. This also requires refreshing the panel explicitly, as
     * otherwise Swing doesn't realize it should trigger a re-layout.
     */
    ImmutableMap.Builder<BoolProperty, ObservableValue<Boolean>> layoutPropertiesBuilder = ImmutableMap.builder();

    // Show and hide selected asset types for foreground layer
    layoutPropertiesBuilder.put(new VisibleProperty(myForegroundImageAssetRowPanel), new SelectedProperty(myForegroundImageRadioButton));
    layoutPropertiesBuilder.put(
      new VisibleProperty(myForegroundClipartAssetRowPanel), new SelectedProperty(myForegroundClipartRadioButton));
    layoutPropertiesBuilder.put(new VisibleProperty(myForegroundTextAssetRowPanel), new SelectedProperty(myForegroundTextRadioButton));
    Expression<Boolean> isForegroundIsNotImage =
      Expression.create(() -> myForegroundAssetType.get() != ForegroundAssetType.IMAGE, myForegroundAssetType);
    layoutPropertiesBuilder.put(new VisibleProperty(myForegroundColorRowPanel), isForegroundIsNotImage);

    if (HIDE_INAPPLICABLE_CONTROLS) {
      layoutPropertiesBuilder.put(new VisibleProperty(myForegroundScalingTitleSeparator), foregroundIsResizable);
      layoutPropertiesBuilder.put(new VisibleProperty(myForegroundImageOptionsPanel), foregroundIsResizable);
    }
    else {
      layoutPropertiesBuilder.put(new EnabledProperty(myForegroundTrimYesRadioButton), foregroundIsResizable);
      layoutPropertiesBuilder.put(new EnabledProperty(myForegroundTrimNoRadioButton), foregroundIsResizable);
      layoutPropertiesBuilder.put(new EnabledProperty(myForegroundResizeSlider), foregroundIsResizable);
    }

    // Show and hide selected asset types for monochrome layer
    if (myIsMonochromeSupported) {
      layoutPropertiesBuilder.put(new VisibleProperty(myMonochromeImageAssetRowPanel), new SelectedProperty(myMonochromeImageRadioButton));
      layoutPropertiesBuilder.put(
        new VisibleProperty(myMonochromeClipartAssetRowPanel), new SelectedProperty(myMonochromeClipartRadioButton));
      layoutPropertiesBuilder.put(new VisibleProperty(myMonochromeTextAssetRowPanel), new SelectedProperty(myMonochromeTextRadioButton));
      Expression<Boolean> isMonochromeIsNotImage =
        Expression.create(() -> myMonochromeAssetType.get() != MonochromeAssetType.IMAGE, myMonochromeAssetType);
      layoutPropertiesBuilder.put(new VisibleProperty(myMonochromeColorRowPanel), isMonochromeIsNotImage);

      if (HIDE_INAPPLICABLE_CONTROLS) {
        layoutPropertiesBuilder.put(new VisibleProperty(myMonochromeScalingTitleSeparator), monochromeIsResizable);
        layoutPropertiesBuilder.put(new VisibleProperty(myMonochromeImageOptionsPanel), monochromeIsResizable);
      }
      else {
        layoutPropertiesBuilder.put(new EnabledProperty(myMonochromeTrimYesRadioButton), monochromeIsResizable);
        layoutPropertiesBuilder.put(new EnabledProperty(myMonochromeTrimNoRadioButton), monochromeIsResizable);
        layoutPropertiesBuilder.put(new EnabledProperty(myMonochromeResizeSlider), monochromeIsResizable);
      }
    }

    // Show either the image or the color UI controls.
    ObservableBool backgroundIsImage = new SelectedProperty(myBackgroundImageRadioButton);
    ObservableBool backgroundIsColor = new SelectedProperty(myBackgroundColorRadioButton);
    layoutPropertiesBuilder.put(new VisibleProperty(myBackgroundImageAssetRowPanel), backgroundIsImage);
    layoutPropertiesBuilder.put(new VisibleProperty(myBackgroundColorRowPanel), backgroundIsColor);

    if (HIDE_INAPPLICABLE_CONTROLS) {
      layoutPropertiesBuilder.put(new VisibleProperty(myBackgroundScalingTitleSeparator), backgroundIsResizable);
      layoutPropertiesBuilder.put(new VisibleProperty(myBackgroundImageOptionsPanel), backgroundIsResizable);
    }
    else {
      layoutPropertiesBuilder.put(new EnabledProperty(myBackgroundTrimYesRadioButton), backgroundIsResizable);
      layoutPropertiesBuilder.put(new EnabledProperty(myBackgroundTrimNoRadioButton), backgroundIsResizable);
      layoutPropertiesBuilder.put(new EnabledProperty(myBackgroundResizeSlider), backgroundIsResizable);
    }

    layoutPropertiesBuilder.put(new EnabledProperty(myLegacyIconShapeComboBox), new SelectedProperty(myGenerateLegacyIconYesRadioButton));

    ObservableBool isLauncherIcon = new BoolValueProperty(myIconType == AndroidIconType.LAUNCHER);
    layoutPropertiesBuilder.put(new VisibleProperty(myLegacyIconShapeRowPanel), isLauncherIcon);
    layoutPropertiesBuilder.put(new VisibleProperty(myGenerateRoundIconTitle), isLauncherIcon);
    layoutPropertiesBuilder.put(new VisibleProperty(myGenerateRoundIconRowPanel), isLauncherIcon);
    layoutPropertiesBuilder.put(new VisibleProperty(myGeneratePlayStoreIconTitle), isLauncherIcon);
    layoutPropertiesBuilder.put(new VisibleProperty(myGeneratePlayStoreIconRowPanel), isLauncherIcon);

    ImmutableMap<BoolProperty, ObservableValue<Boolean>> layoutProperties = layoutPropertiesBuilder.build();
    for (Map.Entry<BoolProperty, ObservableValue<Boolean>> entry : layoutProperties.entrySet()) {
      // Initialize everything off, as this makes sure the frame that uses this panel won't start
      // REALLY LARGE by default.
      entry.getKey().set(false);
      myGeneralBindings.bind(entry.getKey(), entry.getValue());
    }
    myListeners.listenAll(layoutProperties.keySet()).with(() -> {
      SwingUtilities.updateComponentTreeUI(myForegroundAllOptionsPanel);
      if (myIsMonochromeSupported) {
        SwingUtilities.updateComponentTreeUI(myMonochromeAllOptionsPanel);
      }
      SwingUtilities.updateComponentTreeUI(myBackgroundAllOptionsPanel);
      SwingUtilities.updateComponentTreeUI(myOtherIconsAllOptionsPanel);
    });
  }