5
\$\begingroup\$

My first 'large' project is to create an organizational tool. Right now, it's pretty basic, but nevertheless functional in terms of the intent of the current release (v0.3.0).

My main question is: 'Is what I'm doing procedurally correct/is this the way I should be designing my GUI?'. This is my first time doing any GUI design, and although I feel that the end-product is pretty good for a first attempt, I'm also hopeful that there is something I could/should be doing to either make my code more readable/debuggable or (I guess this is kind of the same thing, but) easier to change in the future.

Secondary to that, is the way I'm handling the JList I'm using for the 'log' the best way to do so? I'd eventually like to make a custom type of list that better handles the use of the LogItem objects that I'm using (in my mind called a LogList that accepts LogItems for a .add(LogItem logItem) method.

package com.t99sdevelopment;

// Created by Trevor Sears <trevorsears.main@gmail.com> @ 11:45AM - March 16th, 2017.

import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.*;

public class Window extends JFrame implements Runnable{

    public static LogListModel log = new LogListModel();

    private static JFrame frame = new JFrame();

        private static JMenuBar menuBar = new JMenuBar();
        private static JMenu file_Menu = new JMenu("File");
            private static JMenuItem open_File_MenuItem = new JMenuItem("Open...");
            private static JMenuItem reset_File_MenuItem = new JMenuItem("Reset Log");
            private static JMenuItem close_File_MenuItem = new JMenuItem("Close");
        private static JMenu edit_Menu = new JMenu("Edit");
            private static JMenuItem undo_Edit_MenuItem = new JMenuItem("Undo");
            private static JMenuItem redo_Edit_MenuItem = new JMenuItem("Redo");
        private static JMenu about_Menu = new JMenu("About");

        private static JPanel panel = new JPanel(new GridBagLayout());
            private static JLabel time_Label = new JLabel();
            private static JButton submit_Button = new JButton();
            private static JTextField event_TextField = new JTextField();
            static JList log_List = new JList(log.toArray()); //not sure if this being public is the best solution to the problem...
                private static JPopupMenu logCell_PopupMenu = new JPopupMenu();
                    private static JMenuItem edit_logCell_MenuItem = new JMenuItem();
                    private static JMenuItem delete_logCell_MenuItem = new JMenuItem();
            private static JScrollPane scrollPane = new JScrollPane(log_List, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            private static JButton close_Button = new JButton();

        private static JDialog edit_Dialog = new JDialog(frame);
            private static JPanel edit_Dialog_Panel = new JPanel();
                private static JTextField edit_Dialog_edit_TextField = new JTextField();
                private static JPanel edit_Dialog_subpanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 0));
                private static JButton edit_Dialog_submit_Button = new JButton();
                private static JButton edit_Dialog_cancel_Button = new JButton();

    private static EventLogListener eventLogActionListener = new EventLogListener();
    private static LogEditorListener logEditorActionListener = new LogEditorListener();
    private static ShutdownListener shutdownActionListener = new ShutdownListener(0);

    private static GridBagConstraints constraints = new GridBagConstraints();
    private static Dimension dimension = new Dimension(500, 200);

    public static void showWindow() {

        initializeWindow();
        frame.setVisible(true);

    }

    private static void initializeWindow(){

        initializeRightMousePopupMenu();
        initializeLogItemEditDialog();
        initializeMenuBar();
        frame.setJMenuBar(menuBar);
        initializePanel();
        frame.add(panel);
        frame.setTitle("organize");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setMinimumSize(dimension);
        frame.setResizable(false);
        frame.pack();
        frame.setLocationRelativeTo(null);
        edit_Dialog.setLocationRelativeTo(null);

    }

    private static void initializePanel(){

        // Time JLabel (time_Label) option setting...
        time_Label.setHorizontalAlignment(JTextField.CENTER);
        time_Label.setFont(new Font("Serif", Font.BOLD, 18));
        constraints.gridx = 0;
        constraints.gridy = 0;
        constraints.gridwidth = 3;
        constraints.gridheight = 1;
        constraints.weightx = 0.5;
        constraints.weighty = 0.05;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.insets = new Insets(5,0,0,0);
        panel.add(time_Label, constraints);

        // Submit JButton (submit_Button) option setting...
        submit_Button.setText("Submit");
        submit_Button.addActionListener(eventLogActionListener);
        constraints.gridx = 0;
        constraints.gridy = 1;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.weightx = 0.05;
        constraints.weighty = 0.1;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.insets = new Insets(0,5,5,3);
        panel.add(submit_Button, constraints);

        // Event JTextField (event_TextField) option setting...
        event_TextField.setColumns(50);
        event_TextField.setEditable(true);
        event_TextField.addActionListener(eventLogActionListener);
        constraints.gridx = 1;
        constraints.gridy = 1;
        constraints.gridwidth = 2;
        constraints.gridheight = 1;
        constraints.weightx = 0.95;
        constraints.weighty = 0.1;
        constraints.fill = GridBagConstraints.HORIZONTAL;
        constraints.insets = new Insets(0,3,5,5);
        panel.add(event_TextField, constraints);

        // Logs JTextArea (logs_TextArea) option setting...
        log_List.setModel(log);
        constraints.gridx = 0;
        constraints.gridy = 2;
        constraints.gridwidth = 3;
        constraints.gridheight = 1;
        constraints.weightx = 0.5;
        constraints.weighty = 0.9;
        constraints.ipady = 40;
        constraints.fill = GridBagConstraints.BOTH;
        constraints.insets = new Insets(0,5,0,5);
        panel.add(scrollPane, constraints);

        // Close JButton (close_Button) option setting...
        close_Button.setText("Close");
        close_Button.addActionListener(shutdownActionListener);
        constraints.gridx = 2;
        constraints.gridy = 3;
        constraints.gridwidth = 1;
        constraints.gridheight = 1;
        constraints.weightx = 1;
        constraints.weighty = 0.05;
        constraints.ipady = 0;
        constraints.fill = GridBagConstraints.NONE;
        constraints.anchor = GridBagConstraints.LINE_END;
        constraints.insets = new Insets(0,0,0,10);
        panel.add(close_Button, constraints);

    }

    private static void initializeMenuBar(){

        open_File_MenuItem.setToolTipText("This doesn't do anything right now!");
        undo_Edit_MenuItem.setToolTipText("This doesn't do anything right now!");
        redo_Edit_MenuItem.setToolTipText("This doesn't do anything right now!");
        about_Menu.setToolTipText("This doesn't do anything right now!");

        reset_File_MenuItem.addActionListener(n -> log.clear());
        close_File_MenuItem.addActionListener(shutdownActionListener);

        file_Menu.add(open_File_MenuItem);
        file_Menu.addSeparator();
        file_Menu.add(reset_File_MenuItem);
        file_Menu.add(close_File_MenuItem);


        edit_Menu.add(undo_Edit_MenuItem);
        edit_Menu.add(redo_Edit_MenuItem);

        menuBar.add(file_Menu);
        menuBar.add(edit_Menu);
        menuBar.add(about_Menu);

    }

    private static void initializeRightMousePopupMenu(){

        log_List.addMouseListener(new MouseAdapter(){

            public void mouseReleased(MouseEvent e){

                if(SwingUtilities.isRightMouseButton(e)){

                    log_List.setSelectedIndex(log_List.locationToIndex(e.getPoint()));
                    edit_Dialog_edit_TextField.setText(log.getEvent(log_List.getSelectedIndex()));
                    logCell_PopupMenu.show(e.getComponent(), e.getX(), e.getY());

                }

            }

        });

        edit_logCell_MenuItem.setText("Edit");
        edit_logCell_MenuItem.addActionListener(e -> edit_Dialog.setVisible(true));
        logCell_PopupMenu.add(edit_logCell_MenuItem);

        delete_logCell_MenuItem.setText("Delete");
        delete_logCell_MenuItem.addActionListener(e -> log.remove(log_List.getSelectedIndex()));
        logCell_PopupMenu.add(delete_logCell_MenuItem);

    }

    private static void initializeLogItemEditDialog(){

        edit_Dialog_Panel.setLayout(new BoxLayout(edit_Dialog_Panel, BoxLayout.Y_AXIS));

        edit_Dialog_Panel.add(Box.createRigidArea(new Dimension(0, 5)));

        // Edit JTextField (edit_Dialog_edit_TextField) option setting...
        edit_Dialog_edit_TextField.setColumns(50);
        edit_Dialog_edit_TextField.addActionListener(logEditorActionListener);
        edit_Dialog_Panel.add(edit_Dialog_edit_TextField);

        edit_Dialog_Panel.add(Box.createRigidArea(new Dimension(0, 5)));

        edit_Dialog_subpanel.add(Box.createRigidArea(new Dimension(250, 0)));

        // Submit JButton (edit_Dialog_submit_Button) option setting...
        edit_Dialog_submit_Button.setText("OK");
        edit_Dialog_submit_Button.addActionListener(logEditorActionListener);
        edit_Dialog_subpanel.add(edit_Dialog_submit_Button);

        // Cancel JButton (edit_Dialog_cancel_Button) option setting...
        edit_Dialog_cancel_Button.setText("Cancel");
        edit_Dialog_cancel_Button.addActionListener(e -> disposeEditDialog());
        edit_Dialog_subpanel.setBackground(new Color(35, 100, 50, 1));
        edit_Dialog_subpanel.add(edit_Dialog_cancel_Button);

        edit_Dialog_Panel.add(edit_Dialog_subpanel);
        edit_Dialog_Panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));

        edit_Dialog.setModal(true);
        edit_Dialog.setSize(new Dimension(500, 150));
        edit_Dialog.setTitle("Edit");
        edit_Dialog.add(edit_Dialog_Panel);
        edit_Dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
        edit_Dialog.pack();

    }

    public static void appendNewEvent(String event){

        log.addElement(new LogItem(event));

        log_List.ensureIndexIsVisible(log_List.getModel().getSize() - 1);

        event_TextField.setText("");

    }

    public static String getEventLogText(){

        return event_TextField.getText();

    }

    public static String getEditedDialogText(){

        return edit_Dialog_edit_TextField.getText();

    }

    public static void disposeEditDialog(){

        edit_Dialog.dispose();

    }

    public void run() {

        while(true){

            time_Label.setText(DateChanger.getTime());

        }

    }

}

The rest of the project can be found here. The link is more for the curious than anything - it includes pre-compiled binaries of a small handful of previous releases.

EDIT: To help ease those of you that are cringing - I've implemented most, if not all of your suggested changes. See the above link if you're interested.

\$\endgroup\$
  • 2
    \$\begingroup\$ Seriously no. You should respect the Single Responsibility Principle but having a class that extends JFrame and implements Runnable is already a sign that your are not. \$\endgroup\$ – gervais.b Mar 28 '17 at 6:20
  • \$\begingroup\$ Calling time_Label.setText in an infinite loop without any pauses is horrible for performance, wasting a whole core. \$\endgroup\$ – D. Jurcau May 28 '17 at 12:04
  • \$\begingroup\$ @D.Jurcau so would your suggested fix just be to wait 1000ms? \$\endgroup\$ – Trevor Sears May 29 '17 at 13:52
  • \$\begingroup\$ @D.Jurcau A timer seems to help: line 28 github.com/T99-/organize/blob/laptop/src/main/java/com/… \$\endgroup\$ – Trevor Sears May 29 '17 at 14:09
  • \$\begingroup\$ Yeah, too much code in one big blob. I would personally break this up into a series of smaller JPanels and then also use a GUI builder tool like the one in NetBeans to do the actual layout. netbeans.org/features/java/swing.html \$\endgroup\$ – markspace Dec 25 '17 at 19:04
0
\$\begingroup\$

First of all you should not mix responsibilities to your Window. Follow the naming conventions and avoid static. Think also about components and testability.

Ideally you should create components for each component of your app, Window, Menu, Editor, Content, and PopupMenu. You can also consider the DateChanger as another model that fires events each seconds and try to keep all the 'logic' of adding and editing an event to the LogListModel.

\$\endgroup\$
0
\$\begingroup\$

Having everything static in a class that extends JFrame is really bad.

\$\endgroup\$
0
\$\begingroup\$

The below screen is what I got from your code (by putting empty/dummy implementations of the classes you have not shared). enter image description here

Just remove all the GridBagLayout and use MigLayout. MigLayout replaces almost every other LayoutManager in Swing.

MigLayout has two version - one for SWT and one for Swing. Be sure to use use the MigLayout core and MigLayout swing jars. And also, while importing, import the Swing version in your code.

You can reduce that 70 lines of initializePanel to 10, and it will be future-proof as well.

I think it has a friendly licence.. But, you would have to check the licence details.

EDIT: Here is the initializePanels method with MigLayout:

private static void initializePanel(){
    panel.setLayout(new MigLayout("insets 5", "[100::, fill]5[grow, fill][]", ""));

    // Time JLabel (time_Label) option setting...
    time_Label.setHorizontalAlignment(JTextField.CENTER);
    time_Label.setFont(new Font("Serif", Font.BOLD, 18));
    panel.add(time_Label, "span 3, wrap");

    // Submit JButton (submit_Button) option setting...
    submit_Button.setText("Submit");
    submit_Button.addActionListener(eventLogActionListener);
    panel.add(submit_Button, "");

    // Event JTextField (event_TextField) option setting...
    event_TextField.setColumns(50);
    event_TextField.setEditable(true);
    event_TextField.addActionListener(eventLogActionListener);
    panel.add(event_TextField, "span 2, wrap");

    // Logs JTextArea (logs_TextArea) option setting...
    log_List.setModel(log);
    panel.add(scrollPane, "span 3, wrap");

    // Close JButton (close_Button) option setting...
    close_Button.setText("Close");
    close_Button.addActionListener(shutdownActionListener);
    panel.add(close_Button, "skip 2");

}

And the two windows next to each other: enter image description here

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.