mirror of
https://github.com/Akomry/sae302_applicom.git
synced 2025-12-06 11:43:53 +00:00
feat(contacts): ajout de la classe Contact.java, ContactMap.java, gestion des objets Contact dans l'interface graphique
This commit is contained in:
parent
45f18e9309
commit
e9480590a7
14 changed files with 649 additions and 36 deletions
|
|
@ -3,8 +3,12 @@ module rtgre.chat {
|
|||
requires javafx.fxml;
|
||||
requires java.logging;
|
||||
requires java.desktop;
|
||||
requires javafx.swing;
|
||||
requires net.synedra.validatorfx;
|
||||
|
||||
|
||||
opens rtgre.chat to javafx.fxml;
|
||||
exports rtgre.chat;
|
||||
exports rtgre.chat.graphisme;
|
||||
opens rtgre.chat.graphisme to javafx.fxml;
|
||||
}
|
||||
|
|
@ -15,8 +15,8 @@ import java.util.logging.Logger;
|
|||
|
||||
public class ChatApplication extends Application {
|
||||
public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName());
|
||||
|
||||
public class EssaiLogger {
|
||||
/* . . . */
|
||||
static {
|
||||
try {
|
||||
InputStream is = EssaiLogger.class.getClassLoader()
|
||||
|
|
@ -26,8 +26,8 @@ public class ChatApplication extends Application {
|
|||
LOGGER.log(Level.INFO, "Cannot read configuration file", e);
|
||||
}
|
||||
}
|
||||
/* . . . */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws IOException {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml"));
|
||||
|
|
|
|||
|
|
@ -2,22 +2,47 @@ package rtgre.chat;
|
|||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.event.ActionEvent;
|
||||
import javafx.event.Event;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.FileChooser;
|
||||
import javafx.stage.Stage;
|
||||
import net.synedra.validatorfx.Check;
|
||||
import net.synedra.validatorfx.TooltipWrapper;
|
||||
import net.synedra.validatorfx.Validator;
|
||||
import rtgre.chat.graphisme.ContactListViewCell;
|
||||
import rtgre.modeles.Contact;
|
||||
import rtgre.modeles.ContactMap;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
public class ChatController implements Initializable {
|
||||
|
||||
private static final Pattern LOGIN_PATTERN = Pattern.compile("^([a-z][a-z0-9]{2,7})$");
|
||||
private static final Pattern HOST_PATTERN = Pattern.compile("/^[a-z]*((\\:?)\\d{1,5})?$/gm");
|
||||
public MenuItem hostAddMenuItem;
|
||||
public MenuItem avatarMenuItem;
|
||||
public MenuItem aboutMenuItem;
|
||||
|
|
@ -28,11 +53,15 @@ public class ChatController implements Initializable {
|
|||
public SplitPane exchangeSplitPane;
|
||||
public ListView postListView;
|
||||
public ListView roomsListView;
|
||||
public ListView contactListView;
|
||||
public ListView contactsListView;
|
||||
public TextField messageTextField;
|
||||
public Button sendButton;
|
||||
public Label statusLabel;
|
||||
public Label dateTimeLabel;
|
||||
public Contact contact;
|
||||
private ContactMap contactMap = new ContactMap();
|
||||
private ObservableList<Contact> contactObservableList = FXCollections.observableArrayList();
|
||||
Validator validatorLogin = new Validator();
|
||||
|
||||
|
||||
@Override
|
||||
|
|
@ -53,13 +82,74 @@ public class ChatController implements Initializable {
|
|||
|
||||
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
||||
|
||||
connectionButton.disableProperty().bind(validatorLogin.containsErrorsProperty());
|
||||
connectionButton.selectedProperty().addListener(this::handleConnection);
|
||||
loginTextField.disableProperty().bind(connectionButton.selectedProperty());
|
||||
hostComboBox.disableProperty().bind(connectionButton.selectedProperty());
|
||||
|
||||
avatarMenuItem.setOnAction(this::handleAvatarChange);
|
||||
avatarImageView.setOnMouseClicked(this::handleAvatarChange);
|
||||
|
||||
initContactListView();
|
||||
|
||||
validatorLogin.createCheck()
|
||||
.dependsOn("login", loginTextField.textProperty())
|
||||
.withMethod(this::checkLogin)
|
||||
.decorates(loginTextField)
|
||||
.immediate();
|
||||
|
||||
|
||||
/* /!\ Set-up d'environnement de test /!\ */
|
||||
/* -------------------------------------- */
|
||||
loginTextField.setText("riri");
|
||||
connectionButton.setSelected(true);
|
||||
}
|
||||
|
||||
private void handleAvatarChange(Event event) {
|
||||
/**
|
||||
* Ouvre une fenêtre de dialogue permettant de choisir son avatar
|
||||
*/
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
Stage stage = (Stage) avatarImageView.getScene().getWindow();
|
||||
fileChooser.setTitle("Select Avatar");
|
||||
fileChooser.getExtensionFilters().addAll(
|
||||
new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg")
|
||||
);
|
||||
File selectedFile = fileChooser.showOpenDialog(stage);
|
||||
if (selectedFile != null) {
|
||||
avatarImageView.setImage(new Image(selectedFile.toURI().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void handleConnection(Observable observable) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
if (connectionButton.isSelected()) {
|
||||
java.awt.Image img = SwingFXUtils.fromFXImage(this.avatarImageView.getImage(), null);
|
||||
this.contact = new Contact(loginTextField.getText(), img);
|
||||
contactMap.put(this.contact.getLogin(), this.contact);
|
||||
System.out.println("Nouveau contact : " + contact);
|
||||
System.out.println(contactMap);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkLogin(Check.Context context) {
|
||||
String login = context.get("login");
|
||||
if (!LOGIN_PATTERN.matcher(login).matches()) {
|
||||
context.error("Format de login non respecté");
|
||||
}
|
||||
if (login.equals("system")) {
|
||||
context.error("Le login ne peut pas être system");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void statusNameUpdate(Event event) {
|
||||
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -75,4 +165,20 @@ public class ChatController implements Initializable {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private void initContactListView() {
|
||||
try {
|
||||
contactsListView.setCellFactory(contactListView -> new ContactListViewCell());
|
||||
contactsListView.setItems(contactObservableList);
|
||||
File avatars = new File(getClass().getResource("avatars.png").toURI());
|
||||
Contact riri = new Contact("riri", false, avatars);
|
||||
Contact fifi = new Contact("fifi", true, avatars);
|
||||
contactObservableList.add(riri);
|
||||
contactMap.add(riri);
|
||||
contactObservableList.add(fifi);
|
||||
contactMap.add(fifi);
|
||||
} catch (Exception e) {
|
||||
LOGGER.severe(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
package rtgre.chat.graphisme;
|
||||
|
||||
import rtgre.chat.ChatController;
|
||||
import javafx.embed.swing.SwingFXUtils;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.scene.shape.Circle;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.Text;
|
||||
import rtgre.modeles.Contact;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
import static rtgre.chat.ChatApplication.LOGGER;
|
||||
|
||||
/**
|
||||
* Classe modélisant la fabrique de cellule de la vue des contacts
|
||||
* visibles/connectés {@link ChatController#contactsListView}.
|
||||
*
|
||||
* @see ListCell
|
||||
*/
|
||||
public class ContactListViewCell extends ListCell<Contact> {
|
||||
|
||||
/**
|
||||
* Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
|
||||
*
|
||||
* @param contact Le contact à mettre à jour
|
||||
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||
*/
|
||||
@Override
|
||||
protected void updateItem(Contact contact, boolean empty) {
|
||||
super.updateItem(contact, empty);
|
||||
if (empty) {
|
||||
setGraphic(null);
|
||||
}
|
||||
else {
|
||||
// Cas d'un contact
|
||||
updateContact(contact);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mise à jour de la cellule d'un contact.
|
||||
*
|
||||
* @param contact Le contact à mettre à jour
|
||||
*/
|
||||
|
||||
private void updateContact(Contact contact) {
|
||||
LOGGER.finest("Mise à jour de " + contact);
|
||||
|
||||
String unreadCountNotif = (contact.getUnreadCount() == 0) ? "" : " (%d)".formatted(contact.getUnreadCount());
|
||||
LOGGER.finest("unread: %s %s".formatted(contact.getLogin(), unreadCountNotif));
|
||||
Text loginText = new Text(contact.getLogin() + unreadCountNotif);
|
||||
loginText.setFont(Font.font(null, 12)); // FontWeight.BOLD, 14));
|
||||
loginText.setFill(contact.isConnected() ? Color.BLACK : Color.GRAY);
|
||||
|
||||
Circle circle = new Circle(5, 5, 5);
|
||||
circle.setFill(contact.isConnected() ? Color.CADETBLUE : Color.FIREBRICK);
|
||||
// circle.setOpacity(contact.is_connected() ? 1 : 0.5);
|
||||
|
||||
Image avatar;
|
||||
ImageView view = new ImageView();
|
||||
|
||||
if (contact.getAvatar() != null) {
|
||||
avatar = SwingFXUtils.toFXImage((BufferedImage) contact.getAvatar(), null);
|
||||
view = new ImageView(avatar);
|
||||
}
|
||||
view.setOpacity(contact.isConnected() ? 1 : 0.5);
|
||||
view.setFitWidth(15);
|
||||
view.setFitHeight(15);
|
||||
|
||||
HBox temp = new HBox(circle);
|
||||
temp.setAlignment(Pos.CENTER_RIGHT);
|
||||
HBox.setHgrow(temp, Priority.ALWAYS);
|
||||
HBox hBox = new HBox(view, loginText, temp);
|
||||
hBox.setSpacing(5.0);
|
||||
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||
|
||||
setGraphic(hBox);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -1,18 +1,65 @@
|
|||
package rtgre.modeles;
|
||||
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class Contact {
|
||||
protected String login;
|
||||
protected java.awt.Image avatar;
|
||||
protected boolean connected;
|
||||
protected String currentRoom;
|
||||
|
||||
Contact(String login, java.awt.Image avatar) {
|
||||
public Contact(String login, java.awt.Image avatar) {
|
||||
/**
|
||||
* Crée un objet Contact
|
||||
* @param: String login
|
||||
* @param: java.awt.Image avatar
|
||||
*/
|
||||
this.login = login;
|
||||
this.avatar = avatar;
|
||||
this.connected = false;
|
||||
this.currentRoom = null;
|
||||
}
|
||||
public Contact(String login, boolean connected, java.awt.Image avatar) {
|
||||
/**
|
||||
* Crée un objet Contact
|
||||
* @param: String login
|
||||
* @param: boolean connected
|
||||
* @param: java.awt.Image avatar
|
||||
*/
|
||||
this.login = login;
|
||||
this.avatar = avatar;
|
||||
this.connected = connected;
|
||||
this.currentRoom = null;
|
||||
}
|
||||
|
||||
public String getCurrentRoom() {
|
||||
return currentRoom;
|
||||
}
|
||||
|
||||
public Contact(String login, boolean connected, File banques_avatars) {
|
||||
/**
|
||||
* Crée un objet Contact
|
||||
* @param: String login
|
||||
* @param: boolean connected
|
||||
* @param: File banques_avatars
|
||||
*/
|
||||
this.login = login;
|
||||
try {
|
||||
this.avatar = avatarFromLogin(banques_avatars, login);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Impossible de créer l'utilisateur " + login);
|
||||
System.out.println(e.getMessage());
|
||||
System.out.println(banques_avatars);
|
||||
}
|
||||
this.connected = connected;
|
||||
this.currentRoom = null;
|
||||
}
|
||||
|
||||
|
||||
public String getLogin() {
|
||||
return this.login;
|
||||
|
|
@ -24,20 +71,52 @@ public class Contact {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
return "@" + this.login;
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return this.connected;
|
||||
}
|
||||
|
||||
public void setConnected(boolean connected) {
|
||||
this.connected = connected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Contact contact = (Contact) o;
|
||||
return Objects.equals(login, contact.login);
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {return true;}
|
||||
/*if (this.login == o.login) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hashCode(login);
|
||||
}
|
||||
|
||||
public int getUnreadCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static BufferedImage avatarFromLogin(File fichier, String login) throws IOException {
|
||||
/**
|
||||
* Renvoie une sous-image en fonction d'une banque d'image et d'un login.
|
||||
* @param: File fichier
|
||||
* @param: String login
|
||||
*/
|
||||
BufferedImage img = ImageIO.read(fichier);
|
||||
int width = img.getWidth() / 9;
|
||||
int height = img.getHeight();
|
||||
int n = Integer.remainderUnsigned(login.hashCode(), 9);
|
||||
return img.getSubimage(n*width, 0, width, height);
|
||||
}
|
||||
|
||||
public void setAvatarFromFile(File f) {
|
||||
try {
|
||||
this.avatar = avatarFromLogin(f, this.login);
|
||||
} catch (IOException e) {
|
||||
System.out.println("Erreur : " + e.getMessage());
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
12
chat/src/main/java/rtgre/modeles/ContactMap.java
Normal file
12
chat/src/main/java/rtgre/modeles/ContactMap.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package rtgre.modeles;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class ContactMap extends TreeMap<String, Contact> {
|
||||
public void add(Contact contact) {
|
||||
this.put(contact.login, contact);
|
||||
}
|
||||
|
||||
public Contact getContact(String login) {
|
||||
return this.get(login);
|
||||
}
|
||||
}
|
||||
BIN
chat/src/main/resources/rtgre/chat/avatar1.png
Normal file
BIN
chat/src/main/resources/rtgre/chat/avatar1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
chat/src/main/resources/rtgre/chat/avatar2.png
Normal file
BIN
chat/src/main/resources/rtgre/chat/avatar2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
chat/src/main/resources/rtgre/chat/avatars.png
Normal file
BIN
chat/src/main/resources/rtgre/chat/avatars.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
|
|
@ -1,27 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ListView?>
|
||||
<?import javafx.scene.control.Menu?>
|
||||
<?import javafx.scene.control.MenuBar?>
|
||||
<?import javafx.scene.control.MenuItem?>
|
||||
<?import javafx.scene.control.Separator?>
|
||||
<?import javafx.scene.control.SplitPane?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.control.ToggleButton?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.geometry.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.image.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<VBox alignment="CENTER" minHeight="400.0" minWidth="600.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="rtgre.chat.ChatController">
|
||||
<VBox alignment="CENTER" minHeight="400.0" minWidth="600.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="rtgre.chat.ChatController">
|
||||
<children>
|
||||
<MenuBar>
|
||||
<MenuBar VBox.vgrow="NEVER">
|
||||
<menus>
|
||||
<Menu mnemonicParsing="false" text="Edit">
|
||||
<items>
|
||||
|
|
@ -77,7 +63,7 @@
|
|||
<SplitPane dividerPositions="0.1" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
|
||||
<items>
|
||||
<ListView fx:id="roomsListView" prefHeight="200.0" prefWidth="200.0" />
|
||||
<ListView fx:id="contactListView" prefHeight="200.0" prefWidth="200.0" />
|
||||
<ListView fx:id="contactsListView" prefHeight="200.0" prefWidth="200.0" />
|
||||
</items>
|
||||
</SplitPane>
|
||||
</items>
|
||||
|
|
@ -111,10 +97,10 @@
|
|||
</children>
|
||||
</HBox>
|
||||
<Separator prefWidth="200.0" />
|
||||
<HBox>
|
||||
<HBox VBox.vgrow="NEVER">
|
||||
<children>
|
||||
<Label text="Status : " />
|
||||
<Label fx:id="statusLabel" text="Not connected"/>
|
||||
<Label fx:id="statusLabel" text="Not connected" />
|
||||
<Separator maxWidth="1.7976931348623157E308" orientation="VERTICAL" HBox.hgrow="ALWAYS" />
|
||||
<Label fx:id="dateTimeLabel" text="Today" />
|
||||
</children>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue