First commit of tasks-tracker-plugin-master

This commit is contained in:
2025-10-27 08:31:47 +08:00
parent b80f09a4ea
commit a5aab68ea4
121 changed files with 5353 additions and 3 deletions

View File

@@ -0,0 +1,10 @@
package net.reldo.taskstracker.panel;
import java.awt.Color;
public class Colors {
public static Color QUALIFIED_TEXT_COLOR = new Color(34, 177, 77);
public static Color UNQUALIFIED_BACKGROUND_COLOR = new Color(70, 30, 0);
public static Color UNQUALIFIED_TEXT_COLOR = new Color(251, 93, 93);
public static Color COMPLETED_BACKGROUND_COLOR = new Color(0, 50, 0);
}

View File

@@ -0,0 +1,41 @@
package net.reldo.taskstracker.panel;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.runelite.client.util.ImageUtil;
public class Icons
{
private static final String completeBtnPath = "panel/components/complete_button/";
public static final Icon INCOMPLETE_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, completeBtnPath + "incomplete_only_icon.png"));
public static final Icon COMPLETE_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, completeBtnPath + "complete_only_icon.png"));
public static final Icon COMPLETE_INCOMPLETE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, completeBtnPath + "complete_and_incomplete_icon.png"));
private static final String ignoredBtnPath = "panel/components/ignored_button/";
public static final BufferedImage semivisibleimg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, ignoredBtnPath + "semivisible_icon.png");
public static final Icon SEMIVISIBLE_ICON = new ImageIcon(ImageUtil.alphaOffset(semivisibleimg, -180));
public static final Icon INVISIBLE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, ignoredBtnPath + "invisible_icon.png"));
public static final Icon VISIBLE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, ignoredBtnPath + "visible_icon.png"));
private static final String trackedBtnPath = "panel/components/tracked_button/";
public static final Icon UNTRACKED_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, trackedBtnPath + "untracked_icon.png"));
public static final Icon TRACKED_ONLY_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, trackedBtnPath + "tracked_icon.png"));
public static final Icon TRACKED_UNTRACKED_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, trackedBtnPath + "tracked_and_untracked_icon.png"));
private static final String sortBtnPath = "panel/components/sort_button/";
public static final Icon ASCENDING_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, sortBtnPath + "ascending_icon.png"));
public static final Icon DESCENDING_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, sortBtnPath + "descending_icon.png"));
private static final String expandBtnPath = "panel/components/";
public static final Icon MENU_EXPANDED_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_menu_expanded.png"));
public static final BufferedImage collapseImg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_menu_collapsed.png");
public static final Icon MENU_ICON_HOVER = new ImageIcon(collapseImg);
public static final Icon MENU_COLLAPSED_ICON = new ImageIcon(ImageUtil.alphaOffset(collapseImg, -180));
public static final ImageIcon PLUS_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "plus.png"));
public static final ImageIcon MINUS_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "minus.png"));
public static final ImageIcon EYE_ICON = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "eye.png"));
public static final ImageIcon EYE_CROSS_GREY = new ImageIcon(ImageUtil.loadImageResource(TasksTrackerPlugin.class, "eye-cross-grey.png"));
}

View File

@@ -0,0 +1,543 @@
package net.reldo.taskstracker.panel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.MatteBorder;
import javax.swing.plaf.basic.BasicButtonUI;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerConfig;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.config.ConfigValues;
import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig;
import net.reldo.taskstracker.data.jsondatastore.types.FilterType;
import net.reldo.taskstracker.data.task.TaskService;
import net.reldo.taskstracker.data.task.TaskType;
import net.reldo.taskstracker.panel.components.SearchBox;
import net.reldo.taskstracker.panel.components.TriToggleButton;
import net.reldo.taskstracker.panel.filters.ComboItem;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.SwingUtil;
@Slf4j
public class LoggedInPanel extends JPanel
{
public TaskListPanel taskListPanel;
private JComboBox<ComboItem<TaskType>> taskTypeDropdown;
private final TaskService taskService;
private final TasksTrackerPlugin plugin;
private final TasksTrackerConfig config;
// Filter buttons
private final TriToggleButton completedFilterBtn = new TriToggleButton();
private final TriToggleButton trackedFilterBtn = new TriToggleButton();
private final TriToggleButton ignoredFilterBtn = new TriToggleButton();
private final JPanel titlePanel = new JPanel();
// Task list tabs
private final JPanel tabPane = new JPanel();
// sub-filter panel
private SubFilterPanel subFilterPanel;
private SortPanel sortPanel;
private final JToggleButton collapseBtn = new JToggleButton();
public LoggedInPanel(TasksTrackerPlugin plugin, TasksTrackerConfig config, TaskService taskService)
{
super(false);
this.plugin = plugin;
this.taskService = taskService;
this.config = config;
createPanel();
}
@Override
public Dimension getPreferredSize()
{
return getParent().getSize();
}
public void redraw()
{
// taskTypeDropdown may become de-synced after profile change
String selectedTaskTypeJsonName = taskTypeDropdown.getItemAt(taskTypeDropdown.getSelectedIndex()).getValue().getTaskJsonName();
if(!selectedTaskTypeJsonName.equals(config.taskTypeJsonName()))
{
log.debug("Task type dropdown de-synced, attempting to find current task type");
for(int i = 0; i < taskTypeDropdown.getItemCount(); i++)
{
ComboItem<TaskType> item = taskTypeDropdown.getItemAt(i);
if(item.getValue().getTaskJsonName().equals(config.taskTypeJsonName()))
{
log.debug("Current task type found, setting selected task type");
taskTypeDropdown.setSelectedIndex(i);
break;
}
}
}
subFilterPanel.redraw();
sortPanel.redraw();
updateCollapseButtonText();
taskListPanel.redraw();
}
public void refreshAllTasks()
{
updateCollapseButtonText();
taskListPanel.refreshAllTasks();
}
private void createPanel()
{
setLayout(new BorderLayout());
setBackground(ColorScheme.DARK_GRAY_COLOR);
taskListPanel = new TaskListPanel(plugin, taskService);
add(getNorthPanel(), BorderLayout.NORTH);
add(getCenterPanel(), BorderLayout.CENTER);
add(getSouthPanel(), BorderLayout.SOUTH);
loadAndApplyFilters(config.taskListTab());
if(config.taskListTab().equals(ConfigValues.TaskListTabs.TRACKED))
{
trackedFilterBtn.setState(1);
trackedFilterBtn.setEnabled(false);
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "trackedFilter", ConfigValues.TrackedFilterValues.TRACKED);
}
}
private JPanel getCenterPanel() {
// wrapper for the task list and tab buttons
final JPanel taskListPanel = new JPanel(new BorderLayout());
taskListPanel.setBackground(ColorScheme.DARK_GRAY_COLOR);
taskListPanel.setBorder(new MatteBorder(0, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR));
taskListPanel.setAlignmentX(JPanel.CENTER_ALIGNMENT);
tabPane.setLayout(new BoxLayout(tabPane, BoxLayout.X_AXIS));
tabPane.setBorder(new EmptyBorder(0,0,0,0));
tabPane.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH,24));
JToggleButton trackedTab = tabButton("Tracked Tasks", ConfigValues.TaskListTabs.TRACKED);
JToggleButton allTab = tabButton("All Tasks", ConfigValues.TaskListTabs.ALL);
JToggleButton customTab = tabButton("Custom", ConfigValues.TaskListTabs.CUSTOM);
ButtonGroup tabGroup = new ButtonGroup();
tabGroup.add(trackedTab);
tabGroup.add(allTab);
tabGroup.add(customTab);
tabPane.add(Box.createHorizontalGlue());
tabPane.add(trackedTab);
tabPane.add(Box.createHorizontalGlue());
tabPane.add(allTab);
tabPane.add(Box.createHorizontalGlue());
tabPane.add(customTab);
tabPane.add(Box.createHorizontalGlue());
taskListPanel.add(tabPane, BorderLayout.NORTH);
taskListPanel.add(this.taskListPanel, BorderLayout.CENTER);
// set initial filter states to "complete and incomplete", "tracked and untracked", "not ignored"
Map<String, Integer> filterStates = new HashMap<>();
filterStates.put("completed",0);
filterStates.put("tracked",0);
filterStates.put("ignored",0);
for(ConfigValues.TaskListTabs tab : ConfigValues.TaskListTabs.values())
{
filterStore.put(tab, filterStates);
}
switch (config.taskListTab())
{
case TRACKED:
trackedTab.setSelected(true);
break;
case ALL:
allTab.setSelected(true);
break;
case CUSTOM:
customTab.setSelected(true);
break;
}
tabChanged(config.taskListTab());
return taskListPanel;
}
public void tabChanged(ConfigValues.TaskListTabs newTab)
{
if(newTab != null) {
changeTab(newTab);
switch (newTab) {
case TRACKED:
trackedFilterBtn.setState(1);
trackedFilterBtn.setEnabled(false);
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskListTab", newTab);
filterButtonAction("tracked");
break;
case ALL:
trackedFilterBtn.setState(0);
trackedFilterBtn.setEnabled(false);
completedFilterBtn.setState(0);
completedFilterBtn.setEnabled(false);
ignoredFilterBtn.setState(1);
ignoredFilterBtn.setEnabled(false);
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskListTab", newTab);
actionAllFilterButtons();
break;
case CUSTOM:
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "taskListTab", newTab);
plugin.refreshAllTasks();
break;
default:
plugin.refreshAllTasks();
break;
}
}
}
private JToggleButton tabButton(String label, ConfigValues.TaskListTabs tab)
{
JToggleButton button = new JToggleButton(label);
button.setBorder(new EmptyBorder(2,5,2,5));
button.setBackground(ColorScheme.DARK_GRAY_COLOR);
button.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
button.addActionListener(e -> tabChanged(tab));
return button;
}
private void changeTab(ConfigValues.TaskListTabs newTab)
{
saveFilters();
resetFilters();
loadAndApplyFilters(newTab);
}
private final Map<ConfigValues.TaskListTabs, Map<String, Integer>> filterStore = new HashMap<>();
private void saveFilters()
{
ConfigValues.TaskListTabs tab = config.taskListTab();
Map<String, Integer> filterStates = new HashMap<>();
filterStates.put("completed", config.completedFilter().ordinal());
filterStates.put("tracked", config.trackedFilter().ordinal());
filterStates.put("ignored", config.ignoredFilter().ordinal());
filterStore.put(tab, filterStates);
}
private void resetFilters()
{
completedFilterBtn.setEnabled(true);
trackedFilterBtn.setEnabled(true);
ignoredFilterBtn.setEnabled(true);
}
private void loadAndApplyFilters(ConfigValues.TaskListTabs tab)
{
Map<String,Integer> filterStates = filterStore.get(tab);
if(filterStates == null) return;
Enum configValue;
completedFilterBtn.setState(filterStates.get("completed"));
trackedFilterBtn.setState(filterStates.get("tracked"));
ignoredFilterBtn.setState(filterStates.get("ignored"));
configValue = ConfigValues.CompletedFilterValues.values()[completedFilterBtn.getState()];
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "completedFilter", configValue);
configValue = ConfigValues.TrackedFilterValues.values()[trackedFilterBtn.getState()];
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "trackedFilter", configValue);
configValue = ConfigValues.IgnoredFilterValues.values()[ignoredFilterBtn.getState()];
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "ignoredFilter", configValue);
}
private JPanel getSouthPanel()
{
JPanel southPanel = new JPanel(new BorderLayout());
southPanel.setBorder(new EmptyBorder(5,0,2,0));
JButton importButton = new JButton("Import");
importButton.setBorder(new EmptyBorder(5, 5, 5, 5));
importButton.setLayout(new BorderLayout(0, PluginPanel.BORDER_OFFSET));
importButton.addActionListener(e -> plugin.openImportJsonDialog());
southPanel.add(importButton, BorderLayout.WEST);
JButton totalsButton = new JButton("Totals");
totalsButton.setBorder(new EmptyBorder(5, 5, 5, 5));
totalsButton.setLayout(new BorderLayout(0, PluginPanel.BORDER_OFFSET));
totalsButton.addActionListener(e -> plugin.sendTotalsToChat());
southPanel.add(totalsButton, BorderLayout.CENTER);
JButton exportButton = new JButton("Export");
exportButton.setBorder(new EmptyBorder(5, 5, 5, 5));
exportButton.setLayout(new BorderLayout(0, PluginPanel.BORDER_OFFSET));
exportButton.addActionListener(e -> plugin.copyJsonToClipboard());
southPanel.add(exportButton, BorderLayout.EAST);
return southPanel;
}
private JPanel getNorthPanel()
{
JPanel northPanel = new JPanel();
BoxLayout layout = new BoxLayout(northPanel, BoxLayout.Y_AXIS);
northPanel.setLayout(layout);
northPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
taskTypeDropdown = new JComboBox<>();
taskTypeDropdown.setAlignmentX(LEFT_ALIGNMENT);
taskTypeDropdown.setFocusable(false);
initTaskTypeDropdownAsync();
// Wrapper for collapsible sub-filter menu
JPanel subFilterWrapper = new JPanel();
subFilterWrapper.setLayout(new BorderLayout());
subFilterWrapper.setBorder(new MatteBorder(1, 0, 1, 0, ColorScheme.MEDIUM_GRAY_COLOR));
subFilterWrapper.setAlignmentX(LEFT_ALIGNMENT);
subFilterWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
// collapse button
SwingUtil.removeButtonDecorations(collapseBtn);
collapseBtn.setIcon(Icons.MENU_COLLAPSED_ICON);
collapseBtn.setSelectedIcon(Icons.MENU_EXPANDED_ICON);
collapseBtn.setRolloverIcon(Icons.MENU_ICON_HOVER);
SwingUtil.addModalTooltip(collapseBtn, "Collapse filters", "Expand filters");
collapseBtn.setBackground(ColorScheme.DARKER_GRAY_COLOR);
collapseBtn.setAlignmentX(LEFT_ALIGNMENT);
collapseBtn.setUI(new BasicButtonUI()); // substance breaks the layout
collapseBtn.addActionListener(ev -> subFilterPanel.setVisible(!subFilterPanel.isVisible()));
collapseBtn.setHorizontalTextPosition(JButton.CENTER);
collapseBtn.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
collapseBtn.setFont(FontManager.getRunescapeSmallFont());
collapseBtn.setBorder(new EmptyBorder(2, 0, 2, 0));
collapseBtn.setFocusable(false);
// panel to hold sub-filters
subFilterPanel = new SubFilterPanel(plugin, taskService);
subFilterWrapper.add(collapseBtn, BorderLayout.NORTH);
subFilterWrapper.add(subFilterPanel, BorderLayout.CENTER);
sortPanel = new SortPanel(plugin, taskService, taskListPanel);
northPanel.add(getTitleAndButtonPanel());
northPanel.add(Box.createVerticalStrut(10));
northPanel.add(taskTypeDropdown);
northPanel.add(Box.createVerticalStrut(2));
northPanel.add(getSearchPanel());
northPanel.add(Box.createVerticalStrut(2));
northPanel.add(sortPanel);
northPanel.add(Box.createVerticalStrut(2));
northPanel.add(subFilterWrapper);
return northPanel;
}
private JPanel getTitleAndButtonPanel()
{
titlePanel.setLayout(new BorderLayout());
titlePanel.setBackground(ColorScheme.DARK_GRAY_COLOR);
titlePanel.setPreferredSize(new Dimension(0, 30));
titlePanel.setBorder(new EmptyBorder(5, 5, 5, 10));
JLabel title = new JLabel("Tasks Tracker");
title.setHorizontalAlignment(SwingConstants.LEFT);
title.setForeground(Color.WHITE);
JPopupMenu reloadPluginPopup = new JPopupMenu();
reloadPluginPopup.setBorder(new EmptyBorder(5, 5, 5, 5));
JMenuItem reloadPluginMenuItem = new JMenuItem("Reload plugin");
reloadPluginMenuItem.addActionListener(e -> plugin.reloadTaskType());
reloadPluginPopup.add(reloadPluginMenuItem);
title.setComponentPopupMenu(reloadPluginPopup);
// Filter button bar
final JPanel viewControls = new JPanel();
viewControls.setLayout(new BoxLayout(viewControls, BoxLayout.X_AXIS));
viewControls.setBackground(ColorScheme.DARK_GRAY_COLOR);
// Completed tasks filter button
SwingUtil.removeButtonDecorations(completedFilterBtn);
completedFilterBtn.setIcons(Icons.COMPLETE_INCOMPLETE_ICON, Icons.COMPLETE_ONLY_ICON, Icons.INCOMPLETE_ONLY_ICON);
completedFilterBtn.setToolTips("All tasks", "Completed tasks only", "Incomplete tasks only");
completedFilterBtn.setBackground(ColorScheme.DARK_GRAY_COLOR);
completedFilterBtn.setStateChangedAction(e -> filterButtonAction("completed"));
completedFilterBtn.popupMenuEnabled(true);
completedFilterBtn.setState(config.completedFilter().ordinal());
viewControls.add(completedFilterBtn);
// Tracked tasks filter button
SwingUtil.removeButtonDecorations(trackedFilterBtn);
trackedFilterBtn.setIcons(Icons.TRACKED_UNTRACKED_ICON, Icons.TRACKED_ONLY_ICON, Icons.UNTRACKED_ONLY_ICON);
trackedFilterBtn.setToolTips("All tasks", "Tracked tasks only", "Untracked tasks only");
trackedFilterBtn.setBackground(ColorScheme.DARK_GRAY_COLOR);
trackedFilterBtn.setStateChangedAction(e -> filterButtonAction("tracked"));
trackedFilterBtn.popupMenuEnabled(true);
trackedFilterBtn.setState(config.trackedFilter().ordinal());
viewControls.add(trackedFilterBtn);
// Ignored tasks filter button
SwingUtil.removeButtonDecorations(ignoredFilterBtn);
ignoredFilterBtn.setIcons(Icons.SEMIVISIBLE_ICON, Icons.VISIBLE_ICON, Icons.INVISIBLE_ICON);
ignoredFilterBtn.setToolTips("Hide ignored tasks", "All tasks", "Ignored tasks only");
ignoredFilterBtn.setBackground(ColorScheme.DARK_GRAY_COLOR);
ignoredFilterBtn.setStateChangedAction(e -> filterButtonAction("ignored"));
ignoredFilterBtn.popupMenuEnabled(true);
ignoredFilterBtn.setState(config.ignoredFilter().ordinal());
viewControls.add(ignoredFilterBtn);
titlePanel.add(viewControls, BorderLayout.EAST);
titlePanel.add(title, BorderLayout.WEST);
titlePanel.setAlignmentX(LEFT_ALIGNMENT);
return titlePanel;
}
private void filterButtonActionNoRefresh(String filter)
{
int state;
Enum configValue;
switch (filter)
{
case "completed":
state = completedFilterBtn.getState();
configValue = ConfigValues.CompletedFilterValues.values()[state];
break;
case "tracked":
state = trackedFilterBtn.getState();
configValue = ConfigValues.TrackedFilterValues.values()[state];
break;
case "ignored":
state = ignoredFilterBtn.getState();
configValue = ConfigValues.IgnoredFilterValues.values()[state];
break;
default:
log.debug("Filter button action failed due to unrecognised filter.");
return;
}
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, filter + "Filter", configValue);
}
private void filterButtonAction(String filter)
{
filterButtonActionNoRefresh(filter);
plugin.refreshAllTasks();
}
private void actionAllFilterButtons()
{
filterButtonActionNoRefresh("tracked");
filterButtonActionNoRefresh("ignored");
filterButtonActionNoRefresh("completed");
plugin.refreshAllTasks();
}
private JPanel getSearchPanel()
{
JPanel filtersPanel = new JPanel();
filtersPanel.setAlignmentX(LEFT_ALIGNMENT);
filtersPanel.setLayout(new BoxLayout(filtersPanel, BoxLayout.Y_AXIS));
SearchBox textSearch = new SearchBox();
textSearch.addTextChangedListener(() -> {
plugin.taskTextFilter = textSearch.getText().toLowerCase();
plugin.refreshAllTasks();
});
filtersPanel.add(textSearch);
return filtersPanel;
}
private void updateCollapseButtonText()
{
if (taskService.getCurrentTaskType() == null) return;
ArrayList<FilterConfig> filters = taskService.getCurrentTaskType().getFilters();
int countInclusive = 0;
int countExclusive = 0;
for (FilterConfig filterConfig : filters)
{
String filterText = Optional.ofNullable(plugin.getConfigManager()
.getConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME,
taskService.getCurrentTaskType().getFilterConfigPrefix() + filterConfig.getConfigKey()))
.orElse("");
int count = (filterText.isEmpty() || filterText.equals("-1")) ? 0 : filterText.split(",").length;
if (filterConfig.getFilterType().equals(FilterType.BUTTON_FILTER)) countInclusive += count;
if (filterConfig.getFilterType().equals(FilterType.DROPDOWN_FILTER)) countExclusive += count;
}
collapseBtn.setText(countInclusive + " inclusive, " + countExclusive + " exclusive filters");
}
private void initTaskTypeDropdownAsync() {
TaskType currentTaskType = taskService.getCurrentTaskType();
taskService.getTaskTypesByJsonName().thenAccept(taskTypes -> {
ArrayList<ComboItem<TaskType>> taskTypeItems = new ArrayList<>();
taskTypes.forEach((taskTypeJsonName, taskType) -> {
ComboItem<TaskType> item = new ComboItem<>(taskType, taskType.getName());
taskTypeItems.add(item);
taskTypeDropdown.addItem(item);
});
ComboItem<TaskType> currentTaskTypeComboItem = taskTypeItems.stream()
.filter(item -> item.getValue().equals(currentTaskType))
.findFirst().orElseGet(() -> taskTypeItems.get(0));
taskTypeDropdown.setSelectedItem(currentTaskTypeComboItem);
taskTypeDropdown.addActionListener(e -> {
TaskType taskType = taskTypeDropdown.getItemAt(taskTypeDropdown.getSelectedIndex()).getValue();
taskService.setTaskType(taskType).thenAccept(wasTaskTypeChanged ->{
if (wasTaskTypeChanged) {
SwingUtilities.invokeLater(() ->
{
redraw();
plugin.refreshAllTasks();
});
}
});
});
});
}
}

View File

@@ -0,0 +1,12 @@
package net.reldo.taskstracker.panel;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class LoggedOutPanel extends JPanel
{
public LoggedOutPanel()
{
this.add(new JLabel("Log into an account to track tasks."));
}
}

View File

@@ -0,0 +1,85 @@
package net.reldo.taskstracker.panel;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.JComboBox;
import javax.swing.SwingUtilities;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.config.ConfigValues;
import net.reldo.taskstracker.data.task.TaskService;
import net.reldo.taskstracker.panel.components.FixedWidthPanel;
import net.reldo.taskstracker.panel.components.MultiToggleButton;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.util.SwingUtil;
@Slf4j
public class SortPanel extends FixedWidthPanel
{
private final TasksTrackerPlugin plugin;
private final TaskService taskService;
private final TaskListPanel taskListPanel;
private final ConfigManager configManager;
private JComboBox<String> sortDropdown;
private MultiToggleButton directionButton;
public SortPanel(TasksTrackerPlugin plugin, TaskService taskService, TaskListPanel taskListPanel)
{
this.plugin = plugin;
this.configManager = plugin.getConfigManager();
this.taskService = taskService;
this.taskListPanel = taskListPanel;
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
setAlignmentX(LEFT_ALIGNMENT);
}
public void redraw()
{
removeAll();
List<String> criteriaList = taskService.getSortedIndexes().keySet().stream()
.sorted()
.map((str) -> str.substring(0, 1).toUpperCase() + str.substring(1))
.collect(Collectors.toList());
criteriaList.add(0,"Default");
String[] criteriaArray = criteriaList.toArray(new String[0]);
sortDropdown = new JComboBox<>(criteriaArray);
sortDropdown.setAlignmentX(LEFT_ALIGNMENT);
sortDropdown.setSelectedIndex(0);
sortDropdown.addActionListener(e -> {
updateConfig();
SwingUtilities.invokeLater(taskListPanel::redraw);
});
sortDropdown.setFocusable(false);
directionButton = new MultiToggleButton(2);
SwingUtil.removeButtonDecorations(directionButton);
directionButton.setIcons(new Icon[]{Icons.ASCENDING_ICON, Icons.DESCENDING_ICON});
directionButton.setToolTips(new String[]{"Ascending", "Descending"});
directionButton.setBackground(ColorScheme.DARK_GRAY_COLOR);
directionButton.setStateChangedAction(e -> {
updateConfig();
SwingUtilities.invokeLater(taskListPanel::redraw);
});
add(sortDropdown);
add(directionButton);
updateConfig();
}
protected void updateConfig()
{
log.debug("updateConfig {}, {}, {}", TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortCriteria", sortDropdown.getItemAt(sortDropdown.getSelectedIndex()).toLowerCase());
configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortCriteria", sortDropdown.getItemAt(sortDropdown.getSelectedIndex()).toLowerCase());
ConfigValues.SortDirections configValue = ConfigValues.SortDirections.values()[directionButton.getState()];
log.debug("updateConfig {}, {}, {}", TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortDirection", configValue);
configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, "sortDirection", configValue);
}
}

View File

@@ -0,0 +1,122 @@
package net.reldo.taskstracker.panel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.swing.BoxLayout;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig;
import net.reldo.taskstracker.data.jsondatastore.types.FilterValueType;
import net.reldo.taskstracker.data.task.TaskService;
import net.reldo.taskstracker.panel.components.FixedWidthPanel;
import net.reldo.taskstracker.panel.filters.ComboItem;
import net.reldo.taskstracker.panel.filters.DynamicButtonFilterPanel;
import net.reldo.taskstracker.panel.filters.DynamicDropdownFilterPanel;
import net.reldo.taskstracker.panel.filters.FilterPanel;
import net.runelite.client.ui.ColorScheme;
@Slf4j
public class SubFilterPanel extends FixedWidthPanel
{
private final List<FilterPanel> filterPanels = new ArrayList<>();
private TasksTrackerPlugin plugin;
private TaskService taskService;
public SubFilterPanel(TasksTrackerPlugin plugin, TaskService taskService)
{
this.plugin = plugin;
this.taskService = taskService;
log.debug("SubFilterPanel.constructor");
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder(new EmptyBorder(0, 0, 0, 0));
setBackground(ColorScheme.DARKER_GRAY_COLOR);
setVisible(false);
}
public void redraw()
{
log.debug("SubFilterPanel.redraw"); // TODO: figure out why this calls multiple times upon switching task type
removeAll();
filterPanels.clear();
filterPanels.addAll(getFilterPanels(taskService.getCurrentTaskType().getFilters()));
filterPanels.forEach((filterPanel) -> {
add(filterPanel);
filterPanel.redraw();
});
}
private List<FilterPanel> getFilterPanels(ArrayList<FilterConfig> filterConfigs)
{
List<FilterPanel> filterPanels = new ArrayList<>();
for (FilterConfig filterConfig : filterConfigs)
{
try
{
FilterPanel filterPanel = createDynamicFilterPanel(filterConfig);
if (filterPanel == null)
{
continue;
}
filterPanels.add(filterPanel);
}
catch (Exception ex)
{
log.error("error creating filter panel {} {}", filterConfig.getConfigKey(), ex);
}
}
return filterPanels;
}
private FilterPanel createDynamicFilterPanel(FilterConfig filterConfig) throws Exception
{
switch (filterConfig.getFilterType())
{
case BUTTON_FILTER:
return new DynamicButtonFilterPanel(plugin, filterConfig, taskService.getCurrentTaskType());
case DROPDOWN_FILTER:
ComboItem[] dropdownItems = getDropdownItems(filterConfig);
return new DynamicDropdownFilterPanel(plugin, filterConfig, taskService.getCurrentTaskType(), dropdownItems);
default:
log.error("invalid filter type " + filterConfig.getFilterType());
return null;
}
}
private ComboItem[] getDropdownItems(FilterConfig filterConfig) throws ExecutionException, InterruptedException
{
if (filterConfig.getValueType() == null)
{
throw new Error("invalid filterConfig for dropdown items");
}
if (filterConfig.getValueType().equals(FilterValueType.PARAM_INTEGER))
{
String enumName = filterConfig.getOptionLabelEnum();
if (!enumName.isEmpty())
{
HashMap<Integer, String> enumEntries = taskService.getStringEnumValuesAsync(enumName).get(); // TODO: blocking call
ArrayList<ComboItem<Integer>> options = new ArrayList<>();
options.add(new ComboItem<>(-1, ""));
for (Map.Entry<Integer, String> entry : enumEntries.entrySet())
{
if (filterConfig.getValueName().equals("tier"))
{
if (entry.getValue().equals("All") || entry.getValue().equals("Tier"))
{
continue;
}
}
options.add(new ComboItem<>(entry.getKey(), entry.getValue()));
}
return options.toArray(new ComboItem[0]);
}
}
return new ComboItem[0];
}
}

View File

@@ -0,0 +1,202 @@
package net.reldo.taskstracker.panel;
import java.awt.Component;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.config.ConfigValues;
import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinitionSkill;
import net.reldo.taskstracker.data.task.TaskFromStruct;
import net.reldo.taskstracker.data.task.TaskService;
import net.reldo.taskstracker.panel.components.FixedWidthPanel;
import net.runelite.api.Skill;
import net.runelite.client.ui.FontManager;
@Slf4j
public class TaskListPanel extends JScrollPane
{
public TasksTrackerPlugin plugin;
private final HashMap<Integer, TaskPanel> taskPanelsByStructId = new HashMap<>();
private final TaskListListPanel taskList;
private final TaskService taskService;
private final JLabel emptyTasks = new JLabel();
public TaskListPanel(TasksTrackerPlugin plugin, TaskService taskService)
{
this.plugin = plugin;
taskList = new TaskListListPanel(plugin);
this.taskService = taskService;
setViewportView(taskList);
setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
}
public String getEmptyTaskListMessage()
{
return "No tasks match the current filters.";
}
public void redraw()
{
taskList.redraw();
}
public void refreshAllTasks()
{
log.debug("TaskListPanel.refreshAllTasks");
if (!SwingUtilities.isEventDispatchThread())
{
log.error("Task list panel refresh failed - not event dispatch thread.");
return;
}
for (TaskPanel taskPanel : taskPanelsByStructId.values())
{
taskPanel.refresh();
}
}
public void refreshMultipleTasks(Collection<TaskFromStruct> tasks)
{
log.debug("TaskListPanel.refreshMultipleTasks {}", tasks.size());
if (!SwingUtilities.isEventDispatchThread())
{
log.error("Task list panel refresh failed - not event dispatch thread.");
return;
}
for (TaskFromStruct task : tasks)
{
refresh(task);
}
}
public void refreshTask(TaskFromStruct task)
{
log.debug("TaskListPanel.refreshMultipleTasks {}", task.getName());
refresh(task);
}
private void refresh(TaskFromStruct task)
{
if (!SwingUtilities.isEventDispatchThread())
{
log.error("Task list panel refresh failed - not event dispatch thread.");
return;
}
if (task == null)
{
log.debug("Attempted to refresh null task");
return;
}
emptyTasks.setVisible(false);
TaskPanel panel = taskPanelsByStructId.get(task.getStructId());
if (panel != null)
{
panel.refresh();
}
boolean isAnyTaskPanelVisible = taskPanelsByStructId.values().stream()
.anyMatch(TaskPanel::isVisible);
if (!isAnyTaskPanelVisible)
{
emptyTasks.setVisible(true);
}
}
public void refreshTaskPanelsWithSkill(Skill skill)
{
// Refresh all task panels for tasks with 'skill' or
// 'SKILLS' (any skill) or 'TOTAL LEVEL' as a requirement.
taskPanelsByStructId.values().stream()
.filter(tp ->
{
List<TaskDefinitionSkill> skillsList = tp.task.getTaskDefinition().getSkills();
if (skillsList == null || skillsList.isEmpty())
{
return false;
}
return skillsList.stream()
.map(TaskDefinitionSkill::getSkill)
.anyMatch(s -> s.equalsIgnoreCase(skill.getName()) ||
s.equalsIgnoreCase("SKILLS") ||
s.equalsIgnoreCase("TOTAL LEVEL")
);
})
.forEach(TaskPanel::refresh);
}
private class TaskListListPanel extends FixedWidthPanel
{
private final TasksTrackerPlugin plugin;
public TaskListListPanel(TasksTrackerPlugin plugin)
{
this.plugin = plugin;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBorder(new EmptyBorder(0, 10, 10, 10));
setAlignmentX(Component.LEFT_ALIGNMENT);
emptyTasks.setBorder(new EmptyBorder(10,0,10,0));
emptyTasks.setText("<html><center>" + getEmptyTaskListMessage() + "</center></html>");
emptyTasks.setFont(FontManager.getRunescapeSmallFont());
emptyTasks.setHorizontalAlignment(JLabel.CENTER);
emptyTasks.setVerticalAlignment(JLabel.CENTER);
add(emptyTasks);
emptyTasks.setVisible(false);
}
public void redraw()
{
log.debug("TaskListPanel.redraw");
if(SwingUtilities.isEventDispatchThread())
{
removeAll();
taskPanelsByStructId.clear();
add(emptyTasks);
emptyTasks.setVisible(false);
log.debug("TaskListPanel creating panels");
List<TaskFromStruct> tasks = taskService.getTasks();
if (tasks == null || tasks.isEmpty())
{
emptyTasks.setVisible(true);
return;
}
for (int indexPosition = 0; indexPosition < tasks.size(); indexPosition++)
{
int adjustedIndexPosition = indexPosition;
if (plugin.getConfig().sortDirection().equals(ConfigValues.SortDirections.DESCENDING))
{
adjustedIndexPosition = tasks.size() - (adjustedIndexPosition + 1);
}
TaskFromStruct task = tasks.get(taskService.getSortedTaskIndex(plugin.getConfig().sortCriteria(), adjustedIndexPosition));
TaskPanel taskPanel = new TaskPanel(plugin, task);
add(taskPanel);
taskPanelsByStructId.put(task.getStructId(), taskPanel);
}
log.debug("TaskListPanel validate and repaint");
validate();
repaint();
}
else
{
log.error("Task list panel redraw failed - not event dispatch thread.");
}
}
}
}

View File

@@ -0,0 +1,429 @@
package net.reldo.taskstracker.panel;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JToggleButton;
import javax.swing.JToolTip;
import javax.swing.ToolTipManager;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.HtmlUtil;
import net.reldo.taskstracker.TasksTrackerConfig;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.config.ConfigValues.CompletedFilterValues;
import net.reldo.taskstracker.config.ConfigValues.IgnoredFilterValues;
import net.reldo.taskstracker.config.ConfigValues.TrackedFilterValues;
import net.reldo.taskstracker.data.jsondatastore.types.FilterType;
import net.reldo.taskstracker.data.jsondatastore.types.TaskDefinitionSkill;
import net.reldo.taskstracker.data.task.TaskFromStruct;
import net.reldo.taskstracker.data.task.filters.Filter;
import net.reldo.taskstracker.data.task.filters.ParamButtonFilter;
import net.reldo.taskstracker.data.task.filters.ParamDropdownFilter;
import net.runelite.api.Constants;
import net.runelite.api.Skill;
import net.runelite.client.game.SkillIconManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.util.SwingUtil;
@Slf4j
public class TaskPanel extends JPanel
{
public final TaskFromStruct task;
private final JLabel tierIcon = new JLabel();
private final JPanel container = new JPanel(new BorderLayout());
private final JPanel body = new JPanel(new BorderLayout());
private final JLabel name = new JLabel("task");
private final JLabel description = new JLabel("description");
private final JPanel buttons = new JPanel();
private final JToggleButton toggleTrack = new JToggleButton();
private final JToggleButton toggleIgnore = new JToggleButton();
protected final ArrayList<Filter> filters = new ArrayList<>();
protected TasksTrackerPlugin plugin;
public TaskPanel(TasksTrackerPlugin plugin, TaskFromStruct task)
{
super(new BorderLayout());
this.plugin = plugin;
this.task = task;
createPanel();
setComponentPopupMenu(getPopupMenu());
ToolTipManager.sharedInstance().registerComponent(this);
task.getTaskType().getFilters().forEach((filterConfig) -> {
String paramName = filterConfig.getValueName();
if (filterConfig.getFilterType().equals(FilterType.BUTTON_FILTER))
{
Filter filter = new ParamButtonFilter(plugin.getConfigManager(), paramName, task.getTaskType().getTaskJsonName() + "." + filterConfig.getConfigKey());
filters.add(filter);
}
else if (filterConfig.getFilterType().equals(FilterType.DROPDOWN_FILTER))
{
Filter filter = new ParamDropdownFilter(plugin.getConfigManager(), paramName, task.getTaskType().getTaskJsonName() + "." + filterConfig.getConfigKey());
filters.add(filter);
}
});
refresh();
}
public JPopupMenu getPopupMenu()
{
return null;
}
public String getTaskTooltip()
{
StringBuilder tooltipText = new StringBuilder();
tooltipText.append(HtmlUtil.wrapWithBold(task.getName())).append(HtmlUtil.HTML_LINE_BREAK);
tooltipText.append(task.getDescription()).append(HtmlUtil.HTML_LINE_BREAK);
String skillSection = getSkillSectionHtml();
if (skillSection != null)
{
tooltipText.append(skillSection).append(HtmlUtil.HTML_LINE_BREAK);
}
String wikiNotes = task.getTaskDefinition().getWikiNotes();
if (wikiNotes != null)
{
tooltipText.append(HtmlUtil.HTML_LINE_BREAK).append(wikiNotes).append(HtmlUtil.HTML_LINE_BREAK);
}
if (task.isCompleted())
{
tooltipText.append(HtmlUtil.HTML_LINE_BREAK);
String datePattern = "MM-dd-yyyy hh:mma";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
tooltipText.append("").append(simpleDateFormat.format(new Date(task.getCompletedOn())));
}
Float completionPercent = task.getTaskDefinition().getCompletionPercent();
if (completionPercent != null)
{
tooltipText.append(HtmlUtil.HTML_LINE_BREAK).append("Players Completed: ").append(completionPercent).append('%');
}
return HtmlUtil.wrapWithHtml(
HtmlUtil.wrapWithWrappingParagraph(tooltipText.toString(), 200)
);
}
public Color getTaskBackgroundColor()
{
if (plugin.playerSkills == null)
{
return ColorScheme.DARKER_GRAY_COLOR;
}
if (task.isCompleted())
{
return Colors.COMPLETED_BACKGROUND_COLOR;
}
if (task.getTaskDefinition().getSkills() == null || task.getTaskDefinition().getSkills().size() == 0)
{
return ColorScheme.DARKER_GRAY_COLOR;
}
for (TaskDefinitionSkill requiredSkill : task.getTaskDefinition().getSkills())
{
Skill skill;
String requiredSkillName = requiredSkill.getSkill().toUpperCase();
try
{
skill = Skill.valueOf(requiredSkillName);
}
catch (IllegalArgumentException ex)
{
log.error("invalid skill name " + requiredSkillName);
continue;
}
if (plugin.playerSkills[skill.ordinal()] < requiredSkill.getLevel())
{
return Colors.UNQUALIFIED_BACKGROUND_COLOR;
}
}
return ColorScheme.DARKER_GRAY_COLOR;
}
public void createPanel()
{
setLayout(new BorderLayout());
setBorder(new EmptyBorder(0, 0, 7, 0));
container.setBorder(new EmptyBorder(7, 7, 6, 0));
// Body
name.setFont(FontManager.getRunescapeSmallFont());
name.setForeground(Color.WHITE);
body.add(name, BorderLayout.NORTH);
description.setFont(FontManager.getRunescapeSmallFont());
description.setForeground(Color.GRAY);
body.add(description, BorderLayout.CENTER);
// Buttons
buttons.setLayout(new BoxLayout(buttons, BoxLayout.Y_AXIS));
buttons.setBorder(new EmptyBorder(0, 0, 0, 7));
toggleTrack.setPreferredSize(new Dimension(8, 8));
toggleTrack.setIcon(Icons.PLUS_ICON);
toggleTrack.setSelectedIcon(Icons.MINUS_ICON);
toggleTrack.setBorder(new EmptyBorder(5, 0, 5, 0));
toggleTrack.addActionListener(e -> {
task.setTracked(toggleTrack.isSelected());
plugin.pluginPanel.taskListPanel.refreshTask(task);
plugin.saveCurrentTaskTypeData();
});
SwingUtil.removeButtonDecorations(toggleTrack);
toggleIgnore.setPreferredSize(new Dimension(8, 8));
toggleIgnore.setIcon(Icons.EYE_CROSS_GREY);
toggleIgnore.setSelectedIcon(Icons.EYE_ICON);
SwingUtil.addModalTooltip(toggleIgnore, "Unignore", "Ignore");
toggleIgnore.setBorder(new EmptyBorder(5, 0, 5, 0));
toggleIgnore.addActionListener(e -> {
task.setIgnored(!task.isIgnored());
plugin.pluginPanel.taskListPanel.refreshTask(task);
plugin.saveCurrentTaskTypeData();
});
SwingUtil.removeButtonDecorations(toggleIgnore);
buttons.add(toggleTrack);
buttons.add(toggleIgnore);
// Full
container.add(tierIcon, BorderLayout.WEST);
container.add(body, BorderLayout.CENTER);
container.add(buttons, BorderLayout.EAST);
BufferedImage tierSprite = task.getTaskType().getTierSprites().get(task.getTier());
if (tierSprite != null)
{
tierIcon.setMinimumSize(new Dimension(Constants.ITEM_SPRITE_WIDTH, Constants.ITEM_SPRITE_HEIGHT));
tierIcon.setIcon(new ImageIcon(tierSprite));
tierIcon.setBorder(new EmptyBorder(0, 0, 0, 5));
}
else
{
tierIcon.setBorder(new EmptyBorder(0, 0, 0, 0));
}
add(container, BorderLayout.NORTH);
addMouseListener(new MouseAdapter()
{
@Override
public void mouseReleased(MouseEvent e)
{
if (e.isPopupTrigger())
{
JPopupMenu menu = createWikiPopupMenu();
menu.show(e.getComponent(), e.getX(), e.getY());
}
}
});
}
public JPopupMenu createWikiPopupMenu()
{
JPopupMenu popupMenu = new JPopupMenu();
JMenuItem openWikiItem = new JMenuItem("Wiki");
openWikiItem.addActionListener(e -> openRuneScapeWiki());
popupMenu.add(openWikiItem);
return popupMenu;
}
private void openRuneScapeWiki()
{
String wikiUrl = String.format("https://oldschool.runescape.wiki/%s", URLEncoder.encode(task.getName().replace(' ', '_'), StandardCharsets.UTF_8));
if (Desktop.isDesktopSupported())
{
try
{
Desktop.getDesktop().browse(new URI(wikiUrl));
}
catch (IOException | URISyntaxException ex)
{
ex.printStackTrace();
}
}
else
{
log.warn("Desktop browsing is not supported on this system.");
}
}
public void refresh()
{
setBackgroundColor(getTaskBackgroundColor());
name.setText(HtmlUtil.wrapWithHtml(task.getName()));
description.setText(HtmlUtil.wrapWithHtml(task.getDescription()));
toggleTrack.setSelected(task.isTracked());
toggleIgnore.setSelected(task.isIgnored());
setVisible(meetsFilterCriteria());
revalidate();
}
protected boolean meetsFilterCriteria()
{
String nameLowercase = task.getName().toLowerCase();
String descriptionLowercase = task.getDescription().toLowerCase();
if (plugin.taskTextFilter != null &&
!nameLowercase.contains(plugin.taskTextFilter) &&
!descriptionLowercase.contains(plugin.taskTextFilter))
{
return false;
}
TasksTrackerConfig config = plugin.getConfig();
for (Filter filter : filters)
{
if (!filter.meetsCriteria(task))
{
return false;
}
}
if (config.completedFilter().equals(CompletedFilterValues.INCOMPLETE) && task.isCompleted())
{
return false;
}
if (config.completedFilter().equals(CompletedFilterValues.COMPLETE) && !task.isCompleted())
{
return false;
}
if (config.ignoredFilter().equals(IgnoredFilterValues.NOT_IGNORED) && task.isIgnored())
{
return false;
}
if (config.ignoredFilter().equals(IgnoredFilterValues.IGNORED) && !task.isIgnored())
{
return false;
}
if (config.trackedFilter().equals(TrackedFilterValues.UNTRACKED) && task.isTracked())
{
return false;
}
return !config.trackedFilter().equals(TrackedFilterValues.TRACKED) || task.isTracked();
}
private void setBackgroundColor(Color color)
{
container.setBackground(color);
body.setBackground(color);
buttons.setBackground(color);
}
@Override
public Dimension getMaximumSize()
{
return new Dimension(PluginPanel.PANEL_WIDTH, getPreferredSize().height);
}
@Override
public JToolTip createToolTip()
{
JToolTip customTooltip = new JToolTip();
customTooltip.setFont(FontManager.getRunescapeSmallFont());
return customTooltip;
}
@Override
public String getToolTipText(MouseEvent mouseEvent)
{
return getTaskTooltip();
}
private String getSkillSectionHtml()
{
List<TaskDefinitionSkill> requiredSkills = task.getTaskDefinition().getSkills();
if (requiredSkills == null)
{
return null;
}
StringBuilder skillSection = new StringBuilder();
skillSection.append(HtmlUtil.HTML_LINE_BREAK);
for (TaskDefinitionSkill requiredSkill : requiredSkills)
{
Skill skill;
try
{
skill = Skill.valueOf(requiredSkill.getSkill().toUpperCase());
}
catch (IllegalArgumentException ex)
{
log.warn("unknown skill: {}", requiredSkill.getSkill().toUpperCase(), ex);
continue;
}
Integer requiredLevel = requiredSkill.getLevel();
int playerLevel = -1;
if (requiredLevel == null)
{
continue;
}
if (plugin.playerSkills != null)
{
playerLevel = plugin.playerSkills[skill.ordinal()];
}
String skillMessage = getSkillRequirementHtml(requiredSkill.getSkill().toLowerCase(), playerLevel, requiredLevel);
skillSection.append(skillMessage).append(" ");
}
return skillSection.toString();
}
private String getSkillRequirementHtml(String skillName, Integer playerLevel, int requiredLevel)
{
String skillIconPath = "/skill_icons_small/" + skillName + ".png";
URL url = SkillIconManager.class.getResource(skillIconPath);
Color color = playerLevel >= requiredLevel ? Colors.QUALIFIED_TEXT_COLOR : Colors.UNQUALIFIED_TEXT_COLOR;
return HtmlUtil.imageTag(url) + " " + HtmlUtil.colorTag(color, playerLevel + "/" + requiredLevel);
}
private String getPointsTooltipText()
{
int points = this.task.getPoints();
if (points == 0)
{
return "";
}
return " - " + points + " points";
}
}

View File

@@ -0,0 +1,8 @@
package net.reldo.taskstracker.panel;
import net.reldo.taskstracker.data.task.TaskFromStruct;
public interface TaskPanelFactory
{
TaskPanel create(TaskFromStruct task);
}

View File

@@ -0,0 +1,108 @@
package net.reldo.taskstracker.panel;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerConfig;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.data.task.TaskService;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
@Slf4j
public class TasksTrackerPluginPanel extends PluginPanel
{
private final LoggedInPanel loggedInPanel;
private final LoggedOutPanel loggedOutPanel = new LoggedOutPanel();
public TaskListPanel taskListPanel;
private boolean loggedInPanelVisible = false;
public TasksTrackerPluginPanel(TasksTrackerPlugin plugin, TasksTrackerConfig config, SpriteManager spriteManager, TaskService taskService)
{
super(false);
setBorder(new EmptyBorder(6, 6, 6, 6));
setBackground(ColorScheme.DARK_GRAY_COLOR);
setLayout(new BorderLayout());
loggedInPanel = new LoggedInPanel(plugin, config, taskService);
taskListPanel = loggedInPanel.taskListPanel;
add(loggedInPanel, BorderLayout.NORTH);
loggedInPanel.setVisible(false);
// Add error pane
add(loggedOutPanel);
}
@Override
public Dimension getPreferredSize()
{
return new Dimension(PANEL_WIDTH + SCROLLBAR_WIDTH, super.getPreferredSize().height);
}
public void redraw()
{
if (loggedInPanelVisible)
{
loggedInPanel.redraw();
}
}
public void refreshAllTasks()
{
if (loggedInPanelVisible)
{
loggedInPanel.refreshAllTasks();
}
}
public void setLoggedIn(boolean loggedIn)
{
if(SwingUtilities.isEventDispatchThread())
{
updateVisiblePanel(loggedIn);
}
else
{
log.error("Failed to update loggedIn state - not event dispatch thread.");
}
}
public void hideLoggedInPanel()
{
if(SwingUtilities.isEventDispatchThread())
{
updateVisiblePanel(false);
}
else
{
log.error("Failed to update logged in panel visibility - not event dispatch thread.");
}
}
private void updateVisiblePanel(boolean loggedInPanelVisible)
{
if (loggedInPanelVisible != this.loggedInPanelVisible)
{
if (loggedInPanelVisible)
{
loggedOutPanel.setVisible(false);
loggedInPanel.setVisible(true);
}
else
{
loggedInPanel.setVisible(false);
loggedOutPanel.setVisible(true);
}
validate();
repaint();
}
this.loggedInPanelVisible = loggedInPanelVisible;
}
}

View File

@@ -0,0 +1,42 @@
package net.reldo.taskstracker.panel.components;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import net.runelite.client.ui.ColorScheme;
import static net.runelite.client.ui.PluginPanel.PANEL_WIDTH;
public class CheckBox extends JPanel
{
private final JCheckBox jCheckBox = new JCheckBox();
public CheckBox(String name)
{
setLayout(new BorderLayout());
setMinimumSize(new Dimension(PANEL_WIDTH, 0));
JLabel label = new JLabel(name);
add(label, BorderLayout.CENTER);
jCheckBox.setBackground(ColorScheme.LIGHT_GRAY_COLOR);
add(jCheckBox, BorderLayout.EAST);
}
public boolean isSelected()
{
return jCheckBox.isSelected();
}
public void setSelected(boolean selected)
{
jCheckBox.setSelected(selected);
}
public void addActionListener(ActionListener l)
{
jCheckBox.addActionListener(l);
}
}

View File

@@ -0,0 +1,15 @@
package net.reldo.taskstracker.panel.components;
import java.awt.Dimension;
import javax.swing.JPanel;
import net.runelite.client.ui.PluginPanel;
public class FixedWidthPanel extends JPanel
{
@Override
public Dimension getPreferredSize()
{
return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height);
}
}

View File

@@ -0,0 +1,148 @@
package net.reldo.taskstracker.panel.components;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.border.EmptyBorder;
import lombok.Getter;
import lombok.Setter;
public class MultiToggleButton extends JButton
{
// Icons
private final Icon[] icons;
// Tooltips
private final String[] tooltips;
private final int stateCount;
@Setter
private ActionListener stateChangedAction = null;
@Getter
private int state = 0;
final JPopupMenu popupMenu = new JPopupMenu();
private boolean popupMenuEnabled = false;
public MultiToggleButton(int stateCount)
{
super();
this.stateCount = stateCount;
icons = new Icon[stateCount];
tooltips = new String[stateCount];
popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5));
addActionListener(e -> changeStateThenAction());
}
public void popupMenuEnabled(boolean enabled)
{
if(popupMenuEnabled != enabled)
{
popupMenuEnabled = enabled;
if(popupMenuEnabled)
{
this.setComponentPopupMenu(popupMenu);
}
else
{
this.remove(popupMenu);
}
}
}
public void setIcon(Icon icon, int state)
{
if (state < 0 || state > stateCount || icon == null)
{
return;
}
icons[state] = icon;
if (state == this.state) setIconState();
}
public boolean setIcons(Icon[] icons)
{
if (icons == null || icons.length == 0)
{
return false;
}
for (int i = 0; i < icons.length; i++)
{
setIcon(icons[i], i);
}
return true;
}
public void setToolTip(String tooltip, int state)
{
if (state < 0 || state > 3 || tooltip == null)
{
return;
}
tooltips[state] = tooltip;
addPopupMenuItem(tooltip, state);
if (state == this.state) setTooltipState();
}
public boolean setToolTips(String[] tooltips)
{
if (tooltips == null || tooltips.length == 0)
{
return false;
}
for (int i = 0; i < tooltips.length; i++)
{
setToolTip(tooltips[i], i);
}
return true;
}
public void changeState()
{
setState((++state) % stateCount);
}
public void changeStateThenAction()
{
setStateThenAction((++state) % stateCount);
}
private void setIconState()
{
super.setIcon(icons[state]);
}
private void setTooltipState()
{
super.setToolTipText(tooltips[state]);
}
public void setState(int state)
{
this.state = state;
setIconState();
setTooltipState();
}
public void setStateThenAction(int state)
{
setState(state);
if(stateChangedAction != null) this.stateChangedAction.actionPerformed(new ActionEvent(this, 0, ""));
}
private void addPopupMenuItem(String text, int state)
{
JMenuItem menuItem = new JMenuItem(text);
menuItem.addActionListener(e -> {if(isEnabled())setState(state);});
popupMenu.add(menuItem);
}
}

View File

@@ -0,0 +1,53 @@
package net.reldo.taskstracker.panel.components;
import java.awt.Dimension;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.PluginPanel;
import net.runelite.client.ui.components.IconTextField;
public class SearchBox extends IconTextField
{
private SearchBoxCallback fn;
public SearchBox()
{
this.setIcon(IconTextField.Icon.SEARCH);
this.setPreferredSize(new Dimension(PluginPanel.PANEL_WIDTH - 20, 30));
this.setBackground(ColorScheme.DARKER_GRAY_COLOR);
this.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR);
this.getDocument().addDocumentListener(new DocumentListener()
{
@Override
public void insertUpdate(DocumentEvent documentEvent)
{
fn.call();
}
@Override
public void removeUpdate(DocumentEvent documentEvent)
{
fn.call();
}
@Override
public void changedUpdate(DocumentEvent documentEvent)
{
}
});
this.addActionListener(e -> fn.call()
);
}
public void addTextChangedListener(SearchBoxCallback fn)
{
this.fn = fn;
}
public interface SearchBoxCallback
{
void call();
}
}

View File

@@ -0,0 +1,39 @@
package net.reldo.taskstracker.panel.components;
import javax.swing.Icon;
public class TriToggleButton extends MultiToggleButton
{
public TriToggleButton()
{
super(3);
}
public boolean setIcons(Icon icon0, Icon icon1, Icon icon2)
{
if (icon0 == null || icon1 == null || icon2 == null)
{
return false;
}
setIcon(icon0, 0);
setIcon(icon1, 1);
setIcon(icon2, 2);
return true;
}
public boolean setToolTips(String tooltip0, String tooltip1, String tooltip2)
{
if (tooltip0 == null || tooltip1 == null || tooltip2 == null)
{
return false;
}
setToolTip(tooltip0, 0);
setToolTip(tooltip1, 1);
setToolTip(tooltip2, 2);
return true;
}
}

View File

@@ -0,0 +1,29 @@
package net.reldo.taskstracker.panel.filters;
public class ComboItem<T>
{
private T value;
private String label;
public ComboItem(T value, String label)
{
this.value = value;
this.label = label;
}
public T getValue()
{
return this.value;
}
public String getLabel()
{
return this.label;
}
@Override
public String toString()
{
return label;
}
}

View File

@@ -0,0 +1,131 @@
package net.reldo.taskstracker.panel.filters;
import com.google.common.collect.ImmutableList;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.image.BufferedImage;
import java.util.LinkedHashMap;
import java.util.List;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.border.EmptyBorder;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig;
import net.reldo.taskstracker.data.jsondatastore.types.FilterCustomItem;
import net.reldo.taskstracker.data.task.TaskType;
import net.runelite.client.hiscore.HiscoreSkill;
import static net.runelite.client.hiscore.HiscoreSkill.*;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.util.ImageUtil;
public class DynamicButtonFilterPanel extends FilterButtonPanel
{
private final FilterConfig filterConfig;
private final TaskType taskType;
/**
* Real skills, ordered in the way they should be displayed in the panel.
*/
private static final List<HiscoreSkill> SKILLS = ImmutableList.of(
ATTACK, HITPOINTS, MINING,
STRENGTH, AGILITY, SMITHING,
DEFENCE, HERBLORE, FISHING,
RANGED, THIEVING, COOKING,
PRAYER, CRAFTING, FIREMAKING,
MAGIC, FLETCHING, WOODCUTTING,
RUNECRAFT, SLAYER, FARMING,
CONSTRUCTION, HUNTER
);
public DynamicButtonFilterPanel(TasksTrackerPlugin plugin, FilterConfig filterConfig, TaskType taskType)
{
super(plugin, filterConfig.getLabel());
this.filterConfig = filterConfig;
this.taskType = taskType;
this.configKey = taskType.getFilterConfigPrefix() + filterConfig.getConfigKey();
setLayout(new BorderLayout());
setBackground(ColorScheme.DARKER_GRAY_COLOR);
setBorder(new EmptyBorder(10, 10, 10, 10));
redraw();
}
@Override
protected JPanel makeButtonPanel()
{
// Panel that holds tier icons
JPanel buttonPanel = new JPanel();
buttonPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR);
LinkedHashMap<String, BufferedImage> buttonImages = getIconImages();
LinkedHashMap<String, String> buttonTooltips = getTooltips();
buttonPanel.setLayout(new GridLayout(buttonImages.size() / 3, 3));
// For each filter value create a button and add it to the UI
buttonImages.forEach((key, image) -> {
String tooltip = buttonTooltips.get(key);
JToggleButton button = makeButton(tooltip, image);
button.setSelected(getConfigButtonState(key));
buttons.put(key, button);
buttonPanel.add(button);
});
return buttonPanel;
}
@Override
protected LinkedHashMap<String, BufferedImage> getIconImages()
{
LinkedHashMap<String, BufferedImage> images = new LinkedHashMap<>();
if (filterConfig.getConfigKey().equals("skill"))
{
String skillName;
BufferedImage skillImage;
int index = 0;
for (FilterCustomItem customItem : filterConfig.getCustomItems())
{
if (customItem.getValue() != 0)
{
skillName = SKILLS.get(index).name().toLowerCase();
String directory = "/skill_icons_small/";
String skillIcon = directory + skillName + ".png";
skillImage = ImageUtil.loadImageResource(getClass(), skillIcon);
}
else
{
skillImage = ImageUtil.loadImageResource(TasksTrackerPlugin.class, "panel/components/no_skill.png");
}
String key = customItem.getValue().toString();
images.put(key, skillImage);
index++;
}
}
else
{
for (FilterCustomItem customItem : filterConfig.getCustomItems())
{
String key = customItem.getValue().toString();
images.put(key, taskType.getSpritesById().get(customItem.getSpriteId()));
}
}
return images;
}
private LinkedHashMap<String, String> getTooltips()
{
LinkedHashMap<String, String> tooltips = new LinkedHashMap<>();
for (FilterCustomItem customItem : filterConfig.getCustomItems())
{
String key = customItem.getValue().toString();
tooltips.put(key, customItem.getTooltip());
}
return tooltips;
}
}

View File

@@ -0,0 +1,92 @@
package net.reldo.taskstracker.panel.filters;
import java.awt.GridLayout;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.reldo.taskstracker.data.jsondatastore.types.FilterConfig;
import net.reldo.taskstracker.data.task.TaskType;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
@Slf4j
public class DynamicDropdownFilterPanel extends FilterPanel
{
private final String configKey;
private final FilterConfig filterConfig;
private final ConfigManager configManager;
private final TasksTrackerPlugin plugin;
private final ComboItem[] items;
private JComboBox<ComboItem> dropdown;
public DynamicDropdownFilterPanel(TasksTrackerPlugin plugin, FilterConfig filterConfig, TaskType taskType, ComboItem[] items)
{
this.configManager = plugin.getConfigManager();
this.plugin = plugin;
this.filterConfig = filterConfig;
this.items = items;
this.configKey = taskType.getFilterConfigPrefix() + filterConfig.getConfigKey();
setLayout(new GridLayout(1,2));
setBackground(ColorScheme.DARKER_GRAY_COLOR);
setBorder(new EmptyBorder(5, 10, 5, 10));
}
private JComboBox<ComboItem> makeDropdownPanel()
{
JComboBox<ComboItem> dropdown = new JComboBox<>(items);
dropdown.setFont(FontManager.getRunescapeSmallFont());
dropdown.setAlignmentX(LEFT_ALIGNMENT);
dropdown.setSelectedItem(items[0]);
dropdown.setFocusable(false);
dropdown.setBackground(ColorScheme.DARK_GRAY_COLOR.brighter());
dropdown.addActionListener(e -> {
ComboItem selection = dropdown.getItemAt(dropdown.getSelectedIndex());
updateFilterConfig();
plugin.refreshAllTasks();
log.debug("selected: {} {}", selection.getLabel(), selection.getValue());
});
return dropdown;
}
private JLabel makeLabel()
{
JLabel label = new JLabel(filterConfig.getLabel() + ":");
label.setFont(FontManager.getRunescapeSmallFont());
label.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
return label;
}
protected void updateFilterConfig()
{
log.debug("updateFilterConfig {}, {}, {}", TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey, dropdown.getItemAt(dropdown.getSelectedIndex()).getValue());
configManager.setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey, dropdown.getItemAt(dropdown.getSelectedIndex()).getValue());
}
public void redraw()
{
if(SwingUtilities.isEventDispatchThread())
{
removeAll();
dropdown = makeDropdownPanel();
add(makeLabel());
add(dropdown);
updateFilterConfig();
validate();
repaint();
}
else
{
log.error("Dropdown filter panel redraw failed - not event dispatch thread.");
}
}
}

View File

@@ -0,0 +1,223 @@
package net.reldo.taskstracker.panel.filters;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.stream.Collectors;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicBorders;
import javax.swing.plaf.basic.BasicButtonUI;
import lombok.extern.slf4j.Slf4j;
import net.reldo.taskstracker.TasksTrackerPlugin;
import net.runelite.client.ui.ColorScheme;
import net.runelite.client.ui.FontManager;
import net.runelite.client.util.ImageUtil;
import net.runelite.client.util.SwingUtil;
@Slf4j
public abstract class FilterButtonPanel extends FilterPanel
{
protected final TasksTrackerPlugin plugin;
private final String label;
protected final Map<String, JToggleButton> buttons = new HashMap<>();
protected String configKey;
protected JPanel buttonPanel;
protected JToggleButton collapseBtn;
private final String expandBtnPath = "panel/components/";
private final BufferedImage collapseImg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_buttons_collapsed.png");
private final Icon MENU_COLLAPSED_ICON = new ImageIcon(ImageUtil.alphaOffset(collapseImg, -180));
private final Icon MENU_ICON_HOVER = new ImageIcon(collapseImg);
private final BufferedImage expandedImg = ImageUtil.loadImageResource(TasksTrackerPlugin.class, expandBtnPath + "filter_buttons_expanded.png");
private final Icon MENU_EXPANDED_ICON = new ImageIcon(ImageUtil.alphaOffset(expandedImg, -180));
private final Icon MENU_ICON_HOVER_SELECTED = new ImageIcon(expandedImg);
public FilterButtonPanel(TasksTrackerPlugin plugin, String label)
{
this.plugin = plugin;
this.label = label;
}
protected abstract LinkedHashMap<String, BufferedImage> getIconImages();
protected abstract JPanel makeButtonPanel();
protected JToggleButton makeButton(String tooltip, BufferedImage image)
{
JToggleButton button = new JToggleButton();
button.setBackground(ColorScheme.DARKER_GRAY_COLOR);
button.setBorder(new BasicBorders.ToggleButtonBorder(ColorScheme.DARKER_GRAY_COLOR,
ColorScheme.DARKER_GRAY_COLOR.darker(),
ColorScheme.MEDIUM_GRAY_COLOR.darker(),
ColorScheme.MEDIUM_GRAY_COLOR));
button.setFocusable(false);
if (image != null) {
ImageIcon selectedIcon = new ImageIcon(image);
ImageIcon deselectedIcon = new ImageIcon(ImageUtil.alphaOffset(image, -180));
button.setIcon(deselectedIcon);
button.setSelectedIcon(selectedIcon);
button.setPreferredSize(new Dimension(image.getWidth(), image.getHeight() + 10));
} else {
button.setPreferredSize(new Dimension(button.getPreferredSize().width, 50));
}
button.setToolTipText(tooltip.substring(0,1).toUpperCase() + tooltip.substring(1).toLowerCase());
button.addActionListener(e -> {
updateFilterText();
updateCollapseButtonText();
plugin.refreshAllTasks();
});
button.setSelected(true);
return button;
}
protected JPanel allOrNoneButtons()
{
JPanel buttonWrapper = new JPanel();
buttonWrapper.setLayout(new BoxLayout(buttonWrapper, BoxLayout.X_AXIS));
buttonWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
buttonWrapper.setAlignmentX(JPanel.CENTER_ALIGNMENT);
JButton all = new JButton("all");
SwingUtil.removeButtonDecorations(all);
all.setFocusable(false);
all.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
all.setFont(FontManager.getRunescapeSmallFont());
all.setPreferredSize(new Dimension(50, 0));
all.addActionListener(e -> {
setAllSelected(true);
updateFilterText();
updateCollapseButtonText();
plugin.refreshAllTasks();
});
JButton none = new JButton("none");
SwingUtil.removeButtonDecorations(none);
none.setFocusable(false);
none.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
none.setFont(FontManager.getRunescapeSmallFont());
none.setPreferredSize(new Dimension(50, 0));
none.addActionListener(e -> {
setAllSelected(false);
updateFilterText();
updateCollapseButtonText();
plugin.refreshAllTasks();
});
JLabel separator = new JLabel("|");
separator.setForeground(ColorScheme.MEDIUM_GRAY_COLOR);
buttonWrapper.add(Box.createHorizontalGlue());
buttonWrapper.add(all);
buttonWrapper.add(Box.createHorizontalGlue());
buttonWrapper.add(separator);
buttonWrapper.add(Box.createHorizontalGlue());
buttonWrapper.add(none);
buttonWrapper.add(Box.createHorizontalGlue());
return buttonWrapper;
}
public JToggleButton makeCollapseButton()
{
JToggleButton collapseBtn = new JToggleButton();
// collapse button
SwingUtil.removeButtonDecorations(collapseBtn);
collapseBtn.setIcon(MENU_COLLAPSED_ICON);
collapseBtn.setSelectedIcon(MENU_EXPANDED_ICON);
collapseBtn.setRolloverIcon(MENU_ICON_HOVER);
collapseBtn.setRolloverSelectedIcon(MENU_ICON_HOVER_SELECTED);
SwingUtil.addModalTooltip(collapseBtn, "Collapse filters", "Expand filters");
collapseBtn.setBackground(ColorScheme.DARKER_GRAY_COLOR);
collapseBtn.setAlignmentX(LEFT_ALIGNMENT);
collapseBtn.setUI(new BasicButtonUI()); // substance breaks the layout
collapseBtn.addActionListener(ev -> buttonPanel.setVisible(!buttonPanel.isVisible()));
collapseBtn.setHorizontalTextPosition(JButton.CENTER);
collapseBtn.setForeground(ColorScheme.LIGHT_GRAY_COLOR);
collapseBtn.setFont(FontManager.getRunescapeSmallFont());
collapseBtn.setBorder(new EmptyBorder(2, 0, 2, 0));
collapseBtn.setFocusable(false);
collapseBtn.setSelected(true);
return collapseBtn;
}
protected void updateFilterText()
{
String filterText = buttons.entrySet().stream()
.filter(e -> e.getValue().isSelected())
.map(e -> "f-" + e.getKey() + "-f") // prefix included to cover cases where one key name is contained in another (e.g. "Master" -> "Grandmaster")
.collect(Collectors.joining(","));
plugin.getConfigManager().setConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey, filterText);
}
protected boolean getConfigButtonState(String buttonKey)
{
String configValue = plugin.getConfigManager().getConfiguration(TasksTrackerPlugin.CONFIG_GROUP_NAME, configKey);
boolean isEmptyFilterSelection = configValue == null || configValue.isEmpty() || configValue.equals("-1");
if (isEmptyFilterSelection)
{
return true;
}
return configValue.contains("f-" + buttonKey + "-f");
}
protected void setAllSelected(boolean state)
{
buttons.values().forEach(button -> button.setSelected(state));
}
protected void updateCollapseButtonText()
{
collapseBtn.setText(label + " - " + buttons.values().stream().filter(JToggleButton::isSelected).count() + " / " + buttons.size());
}
public void redraw()
{
if(SwingUtilities.isEventDispatchThread())
{
buttons.clear();
removeAll();
collapseBtn = makeCollapseButton();
buttonPanel = makeButtonPanel();
add(collapseBtn, BorderLayout.NORTH);
add(buttonPanel, BorderLayout.CENTER);
add(allOrNoneButtons(), BorderLayout.SOUTH);
updateFilterText();
updateCollapseButtonText();
collapseBtn.setVisible(plugin.getConfig().filterPanelCollapsible());
validate();
repaint();
}
else
{
log.error("Filter button panel redraw failed - not event dispatch thread.");
}
}
}

View File

@@ -0,0 +1,8 @@
package net.reldo.taskstracker.panel.filters;
import net.reldo.taskstracker.panel.components.FixedWidthPanel;
public abstract class FilterPanel extends FixedWidthPanel
{
public abstract void redraw();
}