/
aleksandrazimut
/
TimerCountdownDesktop
Обзор
Документация
Войти
/
aleksandrazimut
/
TimerCountdownDesktop
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/main/java/com/timerapp/controller/SettingsController.java
479 строк
20 KB
Aleksandr Azimut
init commit
17 дек 2025, 21:59
17 дек 2025, 21:59
3c602f1
Код
Авторство
О чём код?
package com.timerapp.controller; import com.timerapp.model.TimerConfig; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ColorPicker; import javafx.scene.control.ComboBox; import javafx.scene.control.Label; import javafx.scene.control.Slider; import javafx.scene.control.TextField; import javafx.scene.paint.Color; import javafx.stage.FileChooser; import javafx.stage.Stage; import java.io.File; import java.util.function.Consumer; /** * Контроллер окна настроек */ public class SettingsController { @FXML private TextField backgroundPathField; // Путь к фону @FXML private TextField iconPathField; // Путь к иконке @FXML private TextField soundPathField; // Путь к звуку @FXML private TextField customPresetsField; // Пользовательские пресеты @FXML private CheckBox soundEnabledCheckbox; // Включить звук @FXML private Button chooseBackgroundButton; // Кнопка выбора фона @FXML private Button chooseIconButton; // Кнопка выбора иконки @FXML private Button chooseSoundButton; // Кнопка выбора звука @FXML private Button saveButton; // Кнопка сохранения @FXML private Button cancelButton; // Кнопка отмены @FXML private Label statusLabel; // Статус сообщение @FXML private ComboBox<String> buttonGradientCombo; // Градиент кнопок @FXML private ColorPicker buttonColorPicker; // Цвет кнопок @FXML private Slider buttonOpacitySlider; // Слайдер прозрачности кнопок @FXML private Label opacityValueLabel; // Отображение значения прозрачности @FXML private ColorPicker buttonTextColorPicker; // Выбор цвета текста кнопок @FXML private ColorPicker timerTextColorPicker; // Цвет цифр таймера @FXML private ColorPicker progressBarColorPicker; // Цвет прогресс-бара private TimerConfig currentConfig; private Consumer<TimerConfig> onConfigSaved; /** * Инициализация контроллера */ @FXML public void initialize() { // Инициализация ComboBox градиентов if (buttonGradientCombo != null) { buttonGradientCombo.getItems().addAll( "No Gradient", "Gradient H (Light Blue)", "Gradient H (Green)", "Gradient H (Purple)", "Gradient H (Pink)", "Gradient H (Orange)", "Gradient H (Beige)", "Gradient V (Blue-Indigo)", "Gradient V (Green-Teal)", "Gradient V (Red-Pink)", "Gradient V (Purple)"); buttonGradientCombo.setValue("No Gradient"); } // Слушатель для opacity слайдера if (buttonOpacitySlider != null && opacityValueLabel != null) { buttonOpacitySlider.valueProperty().addListener((obs, oldVal, newVal) -> { opacityValueLabel.setText(String.format("%.0f%%", newVal.doubleValue() * 100)); }); } // Слушатель для градиента if (buttonGradientCombo != null && buttonColorPicker != null) { buttonGradientCombo.valueProperty().addListener((obs, oldVal, newVal) -> { // Отключаем выбор цвета если градиент выбран buttonColorPicker.setDisable(newVal != null && !newVal.equals("No Gradient") && !newVal.equals("None")); }); } // При изменении цвета кнопок - сбрасываем градиент if (buttonColorPicker != null && buttonGradientCombo != null) { buttonColorPicker.setOnAction(e -> { buttonGradientCombo.setValue("No Gradient"); }); } } /** * Устанавливает текущую конфигурацию */ public void setCurrentConfig(TimerConfig config) { this.currentConfig = config; loadConfigToUI(); } /** * Устанавливает callback для сохранения конфигурации */ public void setOnConfigSaved(Consumer<TimerConfig> callback) { this.onConfigSaved = callback; } /** * Загружает данные конфигурации в UI */ private void loadConfigToUI() { if (currentConfig != null) { // Загружаем пути к файлам backgroundPathField.setText(currentConfig.getBackgroundPath()); iconPathField.setText(currentConfig.getIconPath()); // Если звук стандартный (из ресурсов), не показываем путь, чтобы был виден // promptText String soundPath = currentConfig.getSoundPath(); if (soundPath != null && !soundPath.startsWith("/sounds/")) { soundPathField.setText(soundPath); } else { soundPathField.clear(); } soundEnabledCheckbox.setSelected(currentConfig.isSoundEnabled()); // Загружаем пресеты (объединяем через запятую) if (currentConfig.getCustomPresets() != null) { String presets = String.join(", ", currentConfig.getCustomPresets()); customPresetsField.setText(presets); } // Градиент String gradient = currentConfig.getButtonGradient(); buttonGradientCombo .setValue((gradient != null && !gradient.isEmpty()) ? convertGradientToDisplay(gradient) : "No Gradient"); // Цвета setPickerColor(buttonColorPicker, currentConfig.getButtonColor(), "#4a5568"); setPickerColor(buttonTextColorPicker, currentConfig.getButtonTextColor(), "#FFFFFF"); setPickerColor(timerTextColorPicker, currentConfig.getTimerTextColor(), "#FFFFFF"); setPickerColor(progressBarColorPicker, currentConfig.getProgressBarColor(), "#4CAF50"); // Прозрачность buttonOpacitySlider.setValue(currentConfig.getButtonOpacity()); } } private void setPickerColor(ColorPicker picker, String hexColor, String defaultHex) { if (picker == null) return; try { if (hexColor != null && !hexColor.isEmpty()) { picker.setValue(Color.web(hexColor)); } else { picker.setValue(Color.web(defaultHex)); } } catch (Exception e) { picker.setValue(Color.web(defaultHex)); } } /** * Конвертирует внутренний градиент в отображаемый (Blue -> Синий) */ private String convertGradientToDisplay(String gradient) { switch (gradient) { case "h_light_blue": return "Gradient H (Light Blue)"; case "h_green": return "Gradient H (Green)"; case "h_purple": return "Gradient H (Purple)"; case "h_pink": return "Gradient H (Pink)"; case "h_orange": return "Gradient H (Orange)"; case "h_beige": return "Gradient H (Beige)"; case "v_blue_indigo": return "Gradient V (Blue-Indigo)"; case "v_green_teal": return "Gradient V (Green-Teal)"; case "v_red_pink": return "Gradient V (Red-Pink)"; case "v_purple_deep": return "Gradient V (Purple)"; default: return "No Gradient"; } } /** * Конвертирует отображаемый градиент во внутренний (Синий -> Blue) */ private String convertGradientToInternal(String display) { switch (display) { case "Gradient H (Light Blue)": return "h_light_blue"; case "Gradient H (Green)": return "h_green"; case "Gradient H (Purple)": return "h_purple"; case "Gradient H (Pink)": return "h_pink"; case "Gradient H (Orange)": return "h_orange"; case "Gradient H (Beige)": return "h_beige"; case "Gradient V (Blue-Indigo)": return "v_blue_indigo"; case "Gradient V (Green-Teal)": return "v_green_teal"; case "Gradient V (Red-Pink)": return "v_red_pink"; case "Gradient V (Purple)": return "v_purple_deep"; default: return "None"; } } /** * Обработчик выбора темы */ @FXML private void onChooseBackground() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select Background Image"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("Images", "*.png", "*.jpg", "*.jpeg", "*.webp"), new FileChooser.ExtensionFilter("PNG files", "*.png"), new FileChooser.ExtensionFilter("JPEG files", "*.jpg", "*.jpeg"), new FileChooser.ExtensionFilter("WEBP files", "*.webp")); File selectedFile = fileChooser.showOpenDialog(getStage()); if (selectedFile != null) { System.out.println("[SettingsController] Selected background file: " + selectedFile.getAbsolutePath()); backgroundPathField.setText(selectedFile.getAbsolutePath()); } } /** * Выбор иконки */ @FXML private void onChooseIcon() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select Icon (PNG)"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("PNG files", "*.png")); File selectedFile = fileChooser.showOpenDialog(getStage()); if (selectedFile != null) { iconPathField.setText(selectedFile.getAbsolutePath()); } } /** * Выбор звукового файла */ @FXML private void onChooseSound() { FileChooser fileChooser = new FileChooser(); fileChooser.setTitle("Select Alarm Sound"); fileChooser.getExtensionFilters().addAll( new FileChooser.ExtensionFilter("Audio files", "*.wav", "*.mp3", "*.ogg"), new FileChooser.ExtensionFilter("WAV files", "*.wav"), new FileChooser.ExtensionFilter("MP3 files", "*.mp3"), new FileChooser.ExtensionFilter("OGG files", "*.ogg")); File selectedFile = fileChooser.showOpenDialog(getStage()); if (selectedFile != null) { soundPathField.setText(selectedFile.getAbsolutePath()); } } /** * Сброс пользовательских настроек оформления кнопок */ @FXML private void onResetButtonStyles() { buttonGradientCombo.setValue("No Gradient"); buttonColorPicker.setValue(Color.web("#4a5568")); buttonTextColorPicker.setValue(Color.WHITE); if (timerTextColorPicker != null) timerTextColorPicker.setValue(Color.WHITE); if (progressBarColorPicker != null) progressBarColorPicker.setValue(Color.web("#4CAF50")); buttonOpacitySlider.setValue(1.0); statusLabel.setText("Styles reset to default"); statusLabel.setStyle("-fx-text-fill: orange;"); } /** * Сохранение настроек */ @FXML private void onSave() { try { String bgPath = backgroundPathField.getText(); System.out.println("[SettingsController] Saving background: " + bgPath); // Создает новую конфигурацию с помощью Builder TimerConfig.Builder builder = new TimerConfig.Builder() .setBackgroundPath(bgPath) .setIconPath(iconPathField.getText()) .setSoundEnabled(soundEnabledCheckbox.isSelected()); // Сохраняет звук: если поле пустое и был стандартный звук - оставляем его String newSoundPath = soundPathField.getText(); if (newSoundPath == null || newSoundPath.isEmpty()) { if (currentConfig.getSoundPath() != null && currentConfig.getSoundPath().startsWith("/sounds/")) { builder.setSoundPath(currentConfig.getSoundPath()); } else { builder.setSoundPath(""); } } else { builder.setSoundPath(newSoundPath); } // Обрабатывает пользовательские пресеты String presetsText = customPresetsField.getText(); if (presetsText != null && !presetsText.isEmpty()) { // Разбивает по запятой или точке с запятой String[] presets = presetsText.split("[,;]+"); // Убирает пробелы из каждого элемента for (int i = 0; i < presets.length; i++) { presets[i] = presets[i].trim(); } // Фильтрует пустые элементы presets = java.util.Arrays.stream(presets) .filter(p -> !p.isEmpty()) .toArray(String[]::new); if (presets.length > 0) { builder.setCustomPresets(presets); System.out.println( "[SettingsController] Custom presets saved: " + String.join(", ", presets)); } } // Определяет themeName: // - Если пользователь установил свой фон ИЛИ иконку - очищаем тему // - Иначе сохраняет текущую тему boolean hasCustomBackground = bgPath != null && !bgPath.isEmpty(); boolean hasCustomIcon = iconPathField.getText() != null && !iconPathField.getText().isEmpty(); if (hasCustomBackground || hasCustomIcon) { // Пользовательский фон или иконка - отключает тему builder.setThemeName(""); System.out.println("[SettingsController] Custom background/icon - theme disabled"); } else { // Сохраняет тему (пользовательские цвета применятся поверх) String currentTheme = (currentConfig != null && currentConfig.getThemeName() != null) ? currentConfig.getThemeName() : ""; builder.setThemeName(currentTheme); System.out.println("[SettingsController] Saving theme: " + currentTheme); } // Сохраняет настройки кнопок String selectedGradient = buttonGradientCombo.getValue(); System.out.println("[SettingsController] selectedGradient FROM ComboBox: " + selectedGradient); // Если ComboBox пустой - используем текущее значение из конфига if (selectedGradient == null && currentConfig != null) { selectedGradient = convertGradientToDisplay(currentConfig.getButtonGradient()); System.out.println("[SettingsController] selectedGradient FROM config: " + selectedGradient); } if (selectedGradient != null && !selectedGradient.equals("No Gradient")) { String internalGradient = convertGradientToInternal(selectedGradient); System.out.println( "[SettingsController] Saving gradient: " + selectedGradient + " -> " + internalGradient); builder.setButtonGradient(internalGradient); builder.setButtonColor("#4a5568"); } else { System.out.println("[SettingsController] Gradient not selected, saving None"); builder.setButtonGradient("None"); builder.setButtonColor(toHexString(buttonColorPicker.getValue())); } builder.setButtonOpacity(buttonOpacitySlider.getValue()); builder.setButtonTextColor(toHexString(buttonTextColorPicker.getValue())); // Новые цвета - сохраняем ТОЛЬКО если они отличаются от дефолтных // ИЛИ если были установлены в текущем конфиге String currentTimerColor = (currentConfig != null) ? currentConfig.getTimerTextColor() : null; String currentProgressColor = (currentConfig != null) ? currentConfig.getProgressBarColor() : null; String newTimerColor = toHexString(timerTextColorPicker.getValue()); String newProgressColor = toHexString(progressBarColorPicker.getValue()); // Сохраняет цвет таймера только если он не дефолтный (#FFFFFF) ИЛИ был в // конфиге if ((currentTimerColor != null && !currentTimerColor.isEmpty()) || (newTimerColor != null && !newTimerColor.equalsIgnoreCase("#FFFFFF"))) { builder.setTimerTextColor(newTimerColor); } else { builder.setTimerTextColor(""); // Пустое = использовать из темы } // Сохраняет цвет прогресс-бара только если он не дефолтный (#4CAF50) ИЛИ был в // конфиге if ((currentProgressColor != null && !currentProgressColor.isEmpty()) || (newProgressColor != null && !newProgressColor.equalsIgnoreCase("#4CAF50"))) { builder.setProgressBarColor(newProgressColor); } else { builder.setProgressBarColor(""); // Пустое = использовать из темы } TimerConfig newConfig = builder.build(); // Вызывает callback if (onConfigSaved != null) { onConfigSaved.accept(newConfig); } statusLabel.setText("✓ Settings saved"); statusLabel.setStyle("-fx-text-fill: green;"); // Закрывает окно через 1 секунду new Thread(() -> { try { Thread.sleep(1000); javafx.application.Platform.runLater(() -> getStage().close()); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } catch (Exception e) { statusLabel.setText("✗ Save error: " + e.getMessage()); statusLabel.setStyle("-fx-text-fill: red;"); e.printStackTrace(); } } private String toHexString(Color color) { if (color == null) return "#FFFFFF"; return String.format("#%02X%02X%02X", (int) (color.getRed() * 255), (int) (color.getGreen() * 255), (int) (color.getBlue() * 255)); } /** * Отмена изменений */ @FXML private void onCancel() { getStage().close(); } /** * Получает текущий Stage */ private Stage getStage() { return (Stage) saveButton.getScene().getWindow(); } }